This commit was manufactured by cvs2svn to create branch 'uc'. mygnokii2002_02_18_02_53
authorjankratochvil <>
Mon, 18 Feb 2002 02:07:04 +0000 (02:07 +0000)
committerjankratochvil <>
Mon, 18 Feb 2002 02:07:04 +0000 (02:07 +0000)
Sprout from mygnokii 2002-02-18 02:07:03 UTC short 'http://marcin-wiacek.fkn.pl/english/zips/mygnokii.tar.gz'
Cherrypick from ats 2002-02-18 02:06:50 UTC short 'http://marcin-wiacek.fkn.pl/english/zips/mygnokii.tar.gz':
    COPYING
    Docs/CREDITS
    Docs/Makefile
    Docs/man/gnokii.1
    Docs/man/gnokiid.8
    Docs/man/mgnokiidev.8
    Docs/man/todologo.1
    Docs/man/xgnokii.1x
    INSTALL
    Makefile
    Makefile.global.in
    VERSION
    common/Makefile
    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/gsm-api.c
    common/gsm-bitmaps.c
    common/gsm-networks.c
    common/gsm-ringtones.c
    common/gsm-sms.c
    common/misc.c
    config/config.guess
    config/config.sub
    config/install-sh
    configure.in
    getopt/Makefile
    getopt/getopt.c
    getopt/getopt.h
    getopt/getopt1.c
    gnokii/Makefile
    gnokii/gnokii.c
    gnokiid/Makefile
    gnokiid/gnokiid.c
    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/gsm-api.h
    include/gsm-bitmaps.h
    include/gsm-common.h
    include/gsm-networks.h
    include/gsm-ringtones.h
    include/gsm-sms.h
    include/misc.h
    mkinstalldirs
    packaging/Debian/README.debian
    packaging/Debian/conffiles
    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
    packaging/Slackware/disk
    packaging/make_dist
    po/Makefile.in.in
    po/POTFILES.in
    po/cs.po
    po/de.po
    po/et.po
    po/fi.po
    po/it.po
    po/nl.po
    po/pl.po
    po/sk.po
    utils/Makefile
    utils/mgnokiidev.c
    utils/todologo
    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_logos.h
    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
    xgnokii/xpm/6110mini.xpm
    xgnokii/xpm/BCard.xpm
    xgnokii/xpm/Black_point.xpm
    xgnokii/xpm/Caller.xpm
    xgnokii/xpm/Caller_logo.xpm
    xgnokii/xpm/Check.xpm
    xgnokii/xpm/Delete.xpm
    xgnokii/xpm/Dial.xpm
    xgnokii/xpm/Display.xpm
    xgnokii/xpm/Duplicate.xpm
    xgnokii/xpm/Edit.xpm
    xgnokii/xpm/Edit_flip_horizontal.xpm
    xgnokii/xpm/Edit_flip_vertical.xpm
    xgnokii/xpm/Edit_invert.xpm
    xgnokii/xpm/Flip.xpm
    xgnokii/xpm/Forward.xpm
    xgnokii/xpm/Green_pixel.xpm
    xgnokii/xpm/Green_point.xpm
    xgnokii/xpm/Invert.xpm
    xgnokii/xpm/Names.xpm
    xgnokii/xpm/New.xpm
    xgnokii/xpm/NewBD.xpm
    xgnokii/xpm/NewCall.xpm
    xgnokii/xpm/NewMeet.xpm
    xgnokii/xpm/NewRem.xpm
    xgnokii/xpm/Op.xpm
    xgnokii/xpm/Open.xpm
    xgnokii/xpm/Operator_logo.xpm
    xgnokii/xpm/Read.xpm
    xgnokii/xpm/Reply.xpm
    xgnokii/xpm/Save.xpm
    xgnokii/xpm/Send.xpm
    xgnokii/xpm/SendSMS.xpm
    xgnokii/xpm/Startup.xpm
    xgnokii/xpm/Startup_logo.xpm
    xgnokii/xpm/Tool_brush.xpm
    xgnokii/xpm/Tool_filled_rectangle.xpm
    xgnokii/xpm/Tool_line.xpm
    xgnokii/xpm/Tool_rectangle.xpm
    xgnokii/xpm/alarm.xpm
    xgnokii/xpm/background.xpm
    xgnokii/xpm/info.xpm
    xgnokii/xpm/logo.xpm
    xgnokii/xpm/phone.xpm
    xgnokii/xpm/quest.xpm
    xgnokii/xpm/sim.xpm
    xgnokii/xpm/sms.xpm
    xgnokii/xpm/stop.xpm

158 files changed:
COPYING [new file with mode: 0644]
Docs/CREDITS [new file with mode: 0644]
Docs/Makefile [new file with mode: 0644]
Docs/man/gnokii.1 [new file with mode: 0644]
Docs/man/gnokiid.8 [new file with mode: 0644]
Docs/man/mgnokiidev.8 [new file with mode: 0644]
Docs/man/todologo.1 [new file with mode: 0644]
Docs/man/xgnokii.1x [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
Makefile.global.in [new file with mode: 0644]
VERSION [new file with mode: 0644]
common/Makefile [new file with mode: 0644]
common/data/at-emulator.c [new file with mode: 0644]
common/data/datapump.c [new file with mode: 0644]
common/data/rlp-common.c [new file with mode: 0644]
common/data/rlp-crc24.c [new file with mode: 0644]
common/data/virtmodem.c [new file with mode: 0644]
common/devices/tekram.c [new file with mode: 0644]
common/devices/unixirda.c [new file with mode: 0644]
common/devices/unixserial.c [new file with mode: 0644]
common/gsm-api.c [new file with mode: 0644]
common/gsm-bitmaps.c [new file with mode: 0644]
common/gsm-networks.c [new file with mode: 0644]
common/gsm-ringtones.c [new file with mode: 0644]
common/gsm-sms.c [new file with mode: 0644]
common/misc.c [new file with mode: 0644]
config/config.guess [new file with mode: 0755]
config/config.sub [new file with mode: 0755]
config/install-sh [new file with mode: 0755]
configure.in [new file with mode: 0644]
getopt/Makefile [new file with mode: 0644]
getopt/getopt.c [new file with mode: 0644]
getopt/getopt.h [new file with mode: 0644]
getopt/getopt1.c [new file with mode: 0644]
gnokii/Makefile [new file with mode: 0644]
gnokii/gnokii.c [new file with mode: 0644]
gnokiid/Makefile [new file with mode: 0644]
gnokiid/gnokiid.c [new file with mode: 0644]
include/config.h.in.in [new file with mode: 0644]
include/data/at-emulator.h [new file with mode: 0644]
include/data/datapump.h [new file with mode: 0644]
include/data/rlp-common.h [new file with mode: 0644]
include/data/rlp-crc24.h [new file with mode: 0644]
include/data/virtmodem.h [new file with mode: 0644]
include/devices/tekram.h [new file with mode: 0644]
include/devices/unixirda.h [new file with mode: 0644]
include/devices/unixserial.h [new file with mode: 0644]
include/gsm-api.h [new file with mode: 0644]
include/gsm-bitmaps.h [new file with mode: 0644]
include/gsm-common.h [new file with mode: 0644]
include/gsm-networks.h [new file with mode: 0644]
include/gsm-ringtones.h [new file with mode: 0644]
include/gsm-sms.h [new file with mode: 0644]
include/misc.h [new file with mode: 0644]
mkinstalldirs [new file with mode: 0644]
packaging/Debian/README.debian [new file with mode: 0644]
packaging/Debian/conffiles [new file with mode: 0644]
packaging/Debian/control [new file with mode: 0644]
packaging/Debian/copyright [new file with mode: 0644]
packaging/Debian/docs [new file with mode: 0644]
packaging/Debian/menu [new file with mode: 0644]
packaging/Debian/postinst [new file with mode: 0644]
packaging/Debian/preinst [new file with mode: 0644]
packaging/Debian/rules [new file with mode: 0755]
packaging/RedHat/gnokii.spec.in [new file with mode: 0644]
packaging/Slackware/SlackBuild.in [new file with mode: 0644]
packaging/Slackware/disk [new file with mode: 0644]
packaging/make_dist [new file with mode: 0755]
po/Makefile.in.in [new file with mode: 0644]
po/POTFILES.in [new file with mode: 0644]
po/cs.po [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/et.po [new file with mode: 0644]
po/fi.po [new file with mode: 0644]
po/it.po [new file with mode: 0644]
po/nl.po [new file with mode: 0644]
po/pl.po [new file with mode: 0644]
po/sk.po [new file with mode: 0644]
utils/Makefile [new file with mode: 0644]
utils/mgnokiidev.c [new file with mode: 0644]
utils/todologo [new file with mode: 0644]
xgnokii/Makefile [new file with mode: 0644]
xgnokii/VERSION [new file with mode: 0644]
xgnokii/xgnokii.c [new file with mode: 0644]
xgnokii/xgnokii.h [new file with mode: 0644]
xgnokii/xgnokii_calendar.c [new file with mode: 0644]
xgnokii/xgnokii_calendar.h [new file with mode: 0644]
xgnokii/xgnokii_cfg.c [new file with mode: 0644]
xgnokii/xgnokii_cfg.h [new file with mode: 0644]
xgnokii/xgnokii_common.c [new file with mode: 0644]
xgnokii/xgnokii_common.h [new file with mode: 0644]
xgnokii/xgnokii_contacts.c [new file with mode: 0644]
xgnokii/xgnokii_contacts.h [new file with mode: 0644]
xgnokii/xgnokii_data.c [new file with mode: 0644]
xgnokii/xgnokii_data.h [new file with mode: 0644]
xgnokii/xgnokii_dtmf.c [new file with mode: 0644]
xgnokii/xgnokii_dtmf.h [new file with mode: 0644]
xgnokii/xgnokii_logos.c [new file with mode: 0644]
xgnokii/xgnokii_logos.h [new file with mode: 0644]
xgnokii/xgnokii_lowlevel.c [new file with mode: 0644]
xgnokii/xgnokii_lowlevel.h [new file with mode: 0644]
xgnokii/xgnokii_netmon.c [new file with mode: 0644]
xgnokii/xgnokii_netmon.h [new file with mode: 0644]
xgnokii/xgnokii_sms.c [new file with mode: 0644]
xgnokii/xgnokii_sms.h [new file with mode: 0644]
xgnokii/xgnokii_speed.c [new file with mode: 0644]
xgnokii/xgnokii_speed.h [new file with mode: 0644]
xgnokii/xgnokii_xkeyb.c [new file with mode: 0644]
xgnokii/xgnokii_xkeyb.h [new file with mode: 0644]
xgnokii/xpm/6110mini.xpm [new file with mode: 0644]
xgnokii/xpm/BCard.xpm [new file with mode: 0644]
xgnokii/xpm/Black_point.xpm [new file with mode: 0644]
xgnokii/xpm/Caller.xpm [new file with mode: 0644]
xgnokii/xpm/Caller_logo.xpm [new file with mode: 0644]
xgnokii/xpm/Check.xpm [new file with mode: 0644]
xgnokii/xpm/Delete.xpm [new file with mode: 0644]
xgnokii/xpm/Dial.xpm [new file with mode: 0644]
xgnokii/xpm/Display.xpm [new file with mode: 0644]
xgnokii/xpm/Duplicate.xpm [new file with mode: 0644]
xgnokii/xpm/Edit.xpm [new file with mode: 0644]
xgnokii/xpm/Edit_flip_horizontal.xpm [new file with mode: 0644]
xgnokii/xpm/Edit_flip_vertical.xpm [new file with mode: 0644]
xgnokii/xpm/Edit_invert.xpm [new file with mode: 0644]
xgnokii/xpm/Flip.xpm [new file with mode: 0644]
xgnokii/xpm/Forward.xpm [new file with mode: 0644]
xgnokii/xpm/Green_pixel.xpm [new file with mode: 0644]
xgnokii/xpm/Green_point.xpm [new file with mode: 0644]
xgnokii/xpm/Invert.xpm [new file with mode: 0644]
xgnokii/xpm/Names.xpm [new file with mode: 0644]
xgnokii/xpm/New.xpm [new file with mode: 0644]
xgnokii/xpm/NewBD.xpm [new file with mode: 0644]
xgnokii/xpm/NewCall.xpm [new file with mode: 0644]
xgnokii/xpm/NewMeet.xpm [new file with mode: 0644]
xgnokii/xpm/NewRem.xpm [new file with mode: 0644]
xgnokii/xpm/Op.xpm [new file with mode: 0644]
xgnokii/xpm/Open.xpm [new file with mode: 0644]
xgnokii/xpm/Operator_logo.xpm [new file with mode: 0644]
xgnokii/xpm/Read.xpm [new file with mode: 0644]
xgnokii/xpm/Reply.xpm [new file with mode: 0644]
xgnokii/xpm/Save.xpm [new file with mode: 0644]
xgnokii/xpm/Send.xpm [new file with mode: 0644]
xgnokii/xpm/SendSMS.xpm [new file with mode: 0644]
xgnokii/xpm/Startup.xpm [new file with mode: 0644]
xgnokii/xpm/Startup_logo.xpm [new file with mode: 0644]
xgnokii/xpm/Tool_brush.xpm [new file with mode: 0644]
xgnokii/xpm/Tool_filled_rectangle.xpm [new file with mode: 0644]
xgnokii/xpm/Tool_line.xpm [new file with mode: 0644]
xgnokii/xpm/Tool_rectangle.xpm [new file with mode: 0644]
xgnokii/xpm/alarm.xpm [new file with mode: 0644]
xgnokii/xpm/background.xpm [new file with mode: 0644]
xgnokii/xpm/info.xpm [new file with mode: 0644]
xgnokii/xpm/logo.xpm [new file with mode: 0644]
xgnokii/xpm/phone.xpm [new file with mode: 0644]
xgnokii/xpm/quest.xpm [new file with mode: 0644]
xgnokii/xpm/sim.xpm [new file with mode: 0644]
xgnokii/xpm/sms.xpm [new file with mode: 0644]
xgnokii/xpm/stop.xpm [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..049061c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,30 @@
+GNOKII - A Linux/Unix toolset and driver for the Nokia 3x10/8110/51x0/61x0
+         Copyright (C) 1999 Hugh Blemings & Pavel Janík ml.
+
+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.
+
+You can contact authors by mail at
+
+hugh@linuxcare.com                    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
+
+We would be delighted to receive a postcard from you if you use gnokii but
+this is not a requirement of these licence terms! :)
+
+Any trademarks in this or other gnokii documents/files are the property of
+their respective owners.
diff --git a/Docs/CREDITS b/Docs/CREDITS
new file mode 100644 (file)
index 0000000..8232bd3
--- /dev/null
@@ -0,0 +1,111 @@
+- Credits -
+
+Thank you to, in no particular order...
+
+*  Everyone on the gnokii mailing list for their interest
+   and input into the project.
+
+*  Pavel Janík ml. for the majority of work on gnokii 6110 series
+   support, spelling corrections and miscellaneous code cleanups.
+
+*  Serge Odinokov for 6110 series authentication protocol and
+   example Delphi RLP code. Many thanks...
+
+*  Nick Lamb for operator logos, CLI uploading and AT*C command support.
+
+*  Francois Dessart, Brendan O'Dea, Tony Lindstrom, Steffan Henke
+   and Cobus Van Eeden for dumps (and in some cases interpretation!)
+   of unknown messages, bug reports and prompt feedback of being
+   able to build new releases.
+
+*  Staffan Ulfberg for taking care of the website and for instigating
+   the 6110 project which has now been merged with the gnokii effort.
+
+*  Martin Hamilton for looking after the mailing list.
+
+*  Tim Potter for some of the ideas that are now incorporated in gnokii
+   to provide multiple model support and config file code.
+
+*  Paul Mackerras and Andrew Tridgell for discussions about pseudo
+   ttys, setuid programs and such.  Tridge kindly reviewed the
+   mgnokiidev.c code.
+
+*  Richard Kalton for his excellent page about Nokia 6110 protocol.
+
+*  Colin Paton for his mail with subject "Dumps of phone<>PC traffic".
+   It helped a lot.
+
+*  Juan Altmayer Pizzorno for his help with MBUS/FBUS and other
+   debugging.
+
+*  Gary Reuter for his help when looking for a bug in phone numbers,
+   for the set-time function debugging.
+
+*  Jiri Meloun for voice calling.
+
+*  Emilio Brambilla for SMS notification fixes, setdatetime implementation
+   and typing GSM_Default_Alphabet.
+
+*  Pawe³ Kot for EnterPIN on 6110 Series changes and other things.
+
+*  Konstantinos Agouros for gnokii-perl module.
+
+*  Czech Linux Users' group for it's CVS server which hosted our
+   development repository.
+
+*  Goran Dokic, Samuli Sorvakko and anonymous for gettext files for
+   Dutch, Finnish and German translations.
+   
+*  Marcel Holtmann for RLP FCS code and misc stuff.
+
+*  Chris Kemp for logo stuff and a big part of the RLP code.
+  
+*  Andrew Kozin for the model 640 support (NMT 450 network).
+*  Karel Zak for his enthusiasm (and patches) for the autoconf stuff.
+  
+*  Harri Yli-Torkko for investigative work & code on the 3810/8110. 
+
+*  Stano Meduna for misc code and build process work.
+  
+*  Alfred Nurnberger for some information on the 5160/6160 MBUS
+   protocol.  
+   
+*  Alessandro Zummo for reset and getopts code.
+
+*  Mark Looi for Cell Broadcast code - based on protocol decoded by
+   Colin Paton
+
+*  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...
+
+*  Last not least: Mirka for allowing Pavel to work on gnokii
+   and for _great_ gnokii operator logos...
+
+*  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...
+
+*  Ladislav Michl & Manfred Jonsson\r for support for various AT phones...
+
+*  Manfred Jonsson for idea of programming, which looks for me good
+
+*  Michael Hund for many improvements and progress in 7110/6210 source...
+
+*  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
+
+*  Gabriele Zappi for many 6210 improvements and big calendar source
+
+*  Andrea Scopece for 61xx source for netmonitor and other improvements
+
+*  ....and other people, who reported me bugs or want(ed) to use mygnokii
+   and help me in making it the best !
+
+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
+name should be mentioned here do not hesitate to contact us :-)
diff --git a/Docs/Makefile b/Docs/Makefile
new file mode 100644 (file)
index 0000000..8dd1724
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# Makefile for the xGNOKII tool suite.
+#
+
+TOPDIR=..
+include $(TOPDIR)/Makefile.global
+
+HELP1_DIR = en_US
+HELP11_DIR = pl_PL
+HELP2_DIR = examples
+HELP3_DIR = default
+
+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"
+
+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)) \
+       fi
+       @echo "done"
+
+clean:
+       $(RM) *~
+
+depend dep:
+       @echo
+
+.PHONY: all install clean dep depend
diff --git a/Docs/man/gnokii.1 b/Docs/man/gnokii.1
new file mode 100644 (file)
index 0000000..214d696
--- /dev/null
@@ -0,0 +1,196 @@
+.TH "gnokii" "1" "July 15, 2000" "" "Gnokii"
+.SH "NAME"
+gnokii \- modem/fax driver for Nokia mobile phones
+.SH "SYNOPSIS"
+.B gnokii
+\fIOPTION\fR [\fIARGUMENT\fR]...
+.SH "DESCRIPTION"
+.PP 
+.B gnokii
+is a Linux/Unix tool suite and (eventually) modem/fax driver for Nokia mobile phones, released under the GPL.
+.PP 
+.B gnokii
+supports most phones from the 3810/8110 and 5110/6110 series, details including bugs specific to each series appear in the files Docs/README\-3810 and Docs/README\-6110 respectively.  
+
+.SH "OPTIONS"
+The options that are recognized by
+.B gnokii
+can be divided into several different groups.
+
+.SS GENERAL
+.TP 
+.BR "\-\-help"
+display usage information.
+.TP 
+.BR "\-\-version"
+displays version and copyright information.
+.TP 
+.BR "\-\-monitor"
+continually updates phone status to stderr.
+
+.SS DIALING
+.TP 
+.BR "\-\-getspeeddial \fIn\fP"
+reads speed dial from the specified location.
+.TP 
+.BR "\-\-setspeeddial \fInumber\fP \fImemory_type\fP \fIlocation\fP"
+specify speed dial.
+.TP 
+.BR "\-\-dialvoice \fInumber\fP"
+initiate voice call.
+.TP 
+.BR "\-\-senddtmf \fIstring\fP"
+send DTMF sequence.
+
+.SS PHONE SETTINGS
+.TP 
+.BR "\-\-getdisplaystatus"
+shows what icons are displayed.
+.TP 
+.BR "\-\-displayoutput"
+show texts displayed in phone's screen.
+.TP 
+.BR "\-\-getprofile [\fInumber\fP]"
+show settings for selected(all) profile(s).
+.TP 
+.BR "\-\-netmonitor {\fIreset\fP|\fIoff\fP|\fIfield\fP|\fIdevel\fP|\fInext\fP|\fInr\fP}"
+setting/querying netmonitor mode.
+.TP 
+.BR "\-\-reset [\fIsoft\fP|\fIhard\fP]"
+resets the phone.
+
+.SS CALENDAR
+.TP 
+.BR "\-\-getcalendarnote \fIindex\fP [\-v]"
+get the note with number \fIindex\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.
+
+.SS SMS
+.TP 
+.BR "\-\-getsms \fImemory_type\fR \fIstart\fP [\fIend\fP] [\-f \fIfile\fP] [\-d]"
+gets SMS messages from specified \fImemory type\fR starting at entry [\fIstart\fR] and ending at [\fIend\fR].
+If [\fIend\fR] is not specified only one location \- [\fIstart\fR] is read.
+If [\-f \fIfile\fR] is used entries are saved in \fIfile\fR.
+Otherwise they are dumped to stdout.
+If [\-d] switch is used, a message is deleted after reading.
+.TP 
+.BR "\-\-deletesms \fImemory_type\fP \fIstart\fP [\fIend\fP]"
+deletes SMS messages from specified \fImemory type\fR starting at entry [\fIstart\fR] and ending at [\fIend\fR].
+If [\fIend\fI] is not specified only one location \- [\fIstart\fR] is read.
+.TP 
+.BR "\-\-sendsms \fIdestination\fP [\-\-smsc \fImessage_center_number\fP | \-\-smscno \fImessage_center_index\fP] [\-r] [\-C \fIn\fP] [\-v \fIn\fP] [\-\-long \fIn\fP]"
+sends an SMS message to \fIdestination\fR via \fImessage_center_number\fR or SMSC number taken from phone memory from address \fImessage_center_index\fR.
+If this argument is ommited SMSC number is taken from phone memory from location 1.
+Message text is taken from STDIN.
+This function has had limited testing and may not work at all on your network.
+Meaning of other optional parameters:
+.PP 
+[\-r] \- request for delivery report
+.PP 
+[\-C \fIn\fR] \- Class Message \fIn\fR, where \fIn\fR can be 0..3
+.PP 
+[\-v \fIn\fR] \- validity in minutes
+.PP 
+[\-\-long \fIn\fR] \- send no more then \fIn\fR characters, default is 160
+.TP 
+.BR "\-\-savesms [\-m] [\-l \fIn\fP] [\-i]"
+saves SMS messages to the Outbox. Messages are read from STDIN. You can specify the following optional arguments:
+.PP 
+[\-m] \- mark the message as sent
+[\-l \fIn\fR] \- save the message at location \fIn\fR on the SIM card
+[\-i] \- ask before overwriting specified location
+.TP 
+.BR "\-\-getsmsc \fInumber\fP"
+show the SMSC number from location \fInumber\fR.
+
+.SS LOGOS
+.TP 
+.BR "\-\-sendlogo {\fIcaller\fP|\fIop\fP} \fIdestination\fP \fIlogofile\fP [\fInetwork_code\fP]"
+send the \fIlogofile\fR to \fIdestination\fR as operator or CLI logo.
+.TP 
+.BR "\-\-setlogo \fIlogofile\fP [\fInetwork_code\fP]"
+.TP 
+.BR "\-\-setlogo \fIlogofile\fP [\fIcaller_group_number\fP] [\fIgroup_name\fP]"
+.TP 
+.BR "\-\-setlogo \fItext\fP [\fIstartup_text\fP]"
+.TP 
+.BR "\-\-setlogo \fIdealer\fP [\fIdealer_startup_text\fP]"
+set caller, startup or operator logo.
+.TP 
+.BR "\-\-getlogo \fIlogofile\fP {\fIcaller\fP|\fIop\fP|\fIstartup\fP} [\fIcaller_group_number\fP]"
+get caller, startup or operator logo.
+
+.SS RINGTONES
+.TP 
+.BR "\-\-sendringtone \fIdestination\fI \fIrtttlfile\fP"
+send the \fIrtttlfile\fR to \fIdestination\fR as ringtone.
+.TP 
+.BR "\-\-setringtone \fIrtttlfile\fP"
+set the \fIrtttlfile\fR as ringtone (on 6110).
+
+.SS PHONEBOOK
+.TP 
+.BR "\-\-getmemory \fImemory_type\fP \fIstart\fP [\fIend\fP]"
+reads specificed memory location from phone.
+If [\fIend\fR] is not specified only one location \- [\fIstart\fR] is read.
+Valid \fImemory types\fR are: ME, SM, FD, ON, EN, DC, RC, MC, LD.
+.TP 
+.BR "\-\-writephonebook [\-i]"
+reads data from stdin and writes to phonebook
+When \-i option is used, refuses to overwrite existing entries.
+Uses the same format as provided by the output of the getphonebook command.
+
+.SS DATE, TIME AND ALARM
+.TP 
+.BR "\-\-setdatetime [\fIYYY\fP [\fIMM\fP [\fIDD\fP [\fIHH\fP [\fIMM\fP]]]]]"
+set the date and the time of the phone.
+.TP 
+.BR "\-\-getdatetime"
+shows current date and time in the phone.
+.TP 
+.BR "\-\-setalarm \fIHH\fP \fIMM\fP"
+set the alarm of the phone.
+.TP 
+.BR "\-\-getalarm"
+shows current alarm.
+
+.SS SECURITY
+.TP 
+.BR "\-\-identify"
+get IMEI, model and revision.
+.TP 
+.BR "\-\-entersecuritycode {\fIPIN\fP|\fIPIN2\fP|\fIPUK\fP|\fIPUK2\fP}"
+asks for the code and sends it to the phone.
+.TP 
+.BR "\-\-getsecuritycodestatus"
+show if a security code is needed.
+
+.SH "DIAGNOSTICS"
+Various error messages are printed to standard error.  The exit code
+is 0 for correct functioning.  Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+
+.SH "BUGS"
+.PP 
+We write quality software here ;)
+
+.SH "AUTHOR"
+Hugh Blemings <hugh@blemings.org> and Pavel Janik ml. <Pavel.Janik@suse.cz>
+
+Manual page written by Dag Wieers <dag@mind.be> and Pawel Kot <pkot@linuxnews.pl>
+
+See also Docs/CREDITS from Gnokii sources.
+
+.SH "COPYING"
+This program is distributed under the GNU Public License.
+
+.SH "SEE ALSO"
+gnokiid, xgnokii, xlogos
diff --git a/Docs/man/gnokiid.8 b/Docs/man/gnokiid.8
new file mode 100644 (file)
index 0000000..99c2b90
--- /dev/null
@@ -0,0 +1,63 @@
+.TH "gnokiid" "8" "May 27, 2001" "" "Gnokii"
+.SH "NAME"
+gnokiid \- create a virtual modem for Nokia portable phones
+.SH "SYNOPSIS"
+.B gnokiid
+
+.B gnokiid
+[\fB\-\-help\fR|\fB\-\-version\fR|\fB\-\-debug\fR]
+
+.SH "DESCRIPTION"
+.PP 
+.B gnokiid
+is a tool from the Gnokii tool suite used to create a virtual modem device with a Nokia portable telephone.  The virtual modem device is usually accessed via a symbolic link called /dev/gnokii (created automatically by \fBmgnokiidev\fR(8)).
+
+.PP 
+After 
+.B gnokiid
+has created the virtual modem, it can be accessed like any other Hayes\-compatible modem by such programs as the terminal program \fBminicom\fR(1) or the PPP daemon \fBpppd\fR(8) to make standard data connections to remote computers.
+
+.PP 
+The full range of portable telephone functions (SMS messaging, phone book, operator logos, etc.) are accessed using the command\-line program \fBgnokii\fR(1) or the graphical interface program \fBxgnokii\fR(1x).
+
+
+.PP 
+Gnokii
+is a Linux/Unix tool suite and (eventually) modem/fax driver for Nokia mobile phones, released under the GPL.
+.PP 
+Gnokii
+supports most phones from the 3810/8110 and 5110/6110 series, details including bugs specific to each series appear in the files Docs/README\-3810 and Docs/README\-6110 respectively.  
+
+.SH "OPTIONS"
+.PP 
+If called without any options, \fBgnokiid\fR will set up a virtual modem device using the settings in /etc/gnokiirc or in the user's ~/.gnokiirc (if it exists).  Otherwise,
+
+.TP 
+\fB\-\-help\fR
+print out a short usage message
+.TP 
+\fB\-\-version\fR
+print out version, copyright, phone, and serial device info
+.TP 
+\fB\-\-debug\fR
+uses STDIN/STDOUT for a virtual modem
+.SH "DIAGNOSTICS"
+Various error messages are printed to STDERR.
+.SH "BUGS"
+.PP 
+Hmmm...
+
+
+.SH "AUTHOR"
+Hugh Blemings <hugh@blemings.org> and Pavel Janik ml. <Pavel.Janik@suse.cz> are the authors of the
+Gnokii
+tool suite.
+
+This manual page was written by Erik Rossen <rossen@freesurf.ch>.
+
+See also Docs/CREDITS from the Gnokii sources.
+.SH "COPYING"
+This program is distributed under the GNU Public License.
+
+.SH "SEE ALSO"
+gnokii, xgnokii, mgnokiidev
diff --git a/Docs/man/mgnokiidev.8 b/Docs/man/mgnokiidev.8
new file mode 100644 (file)
index 0000000..03db0aa
--- /dev/null
@@ -0,0 +1,49 @@
+.TH "mgnokiidev" "8" "May 27, 2001" "" "Gnokii"
+.SH "NAME"
+mgnokiidev \- links /dev/gnokii to a virtual modem
+.SH "SYNOPSIS"
+.B mgnokiidev \fI/dev/pts/?\fR
+
+.SH "DESCRIPTION"
+.PP 
+.B mgnokiidev
+is a tool from the Gnokii tool suite used to create the link /dev/gnokii that points to a pseudo\-tty (/dev/pts/?).
+
+.PP 
+It is called after 
+.B gnokiid
+has created a virtual modem that is attached to the pty.  It's operation is transparent under normal usage as gnokiid takes care of calling it when required.
+
+.PP 
+Since
+.B mgnokiidev
+must modifiy files in /dev, it must be either run as root or SUID root.  The latter is the preferred method.  Normally the 
+.B mgnokiidev
+binary is chmod 4750 and owned by root.gnokii.
+
+.PP 
+Gnokii
+is a Linux/Unix tool suite and (eventually) modem/fax driver for Nokia mobile phones, released under the GPL.
+.PP 
+Gnokii
+supports most phones from the 3810/8110 and 5110/6110 series, details including bugs specific to each series appear in the files Docs/README\-3810 and Docs/README\-6110 respectively.  
+
+.SH "DIAGNOSTICS"
+Various error messages are printed to STDERR.
+.SH "BUGS"
+.PP 
+None reported.
+
+
+.SH "AUTHOR"
+Hugh Blemings <hugh@blemings.org> and Pavel Janik ml. <Pavel.Janik@suse.cz> are the authors of the
+Gnokii tool suite.
+
+This manual page was written by Erik Rossen <rossen@freesurf.ch>.
+
+See also Docs/CREDITS from the Gnokii sources.
+.SH "COPYING"
+This program is distributed under the GNU Public License.
+
+.SH "SEE ALSO"
+gnokiid, gnokii
diff --git a/Docs/man/todologo.1 b/Docs/man/todologo.1
new file mode 100644 (file)
index 0000000..c66955b
--- /dev/null
@@ -0,0 +1,57 @@
+.TH todologo 8 "January 3, 2001" "GNOKII Tools" "GNOKII Tools"
+
+.SH NAME
+todologo \- Create operator logo that contains 3 lines of text
+
+.SH SYNOPSIS
+.B todologo
+\fI<text of line> - <text of line 2> - <text of line 3>\fR
+
+.SH DESCRIPTION
+The 
+.B todologo
+program is used to create three lines of text 
+that fits to the standard Nokia operator logo space (i.e. 72x14 
+pixel space). This is achieved by using a proportional tiny font 
+which most elements have size of 3x4 pixels (some are thinner). 
+
+The name comes from an original intention that this program is
+used to create todo entries to one's mobile phone screen so that
+those are always visible and thus in user's memory. But the tool
+can just be used to print arbitrary text to the space provided
+by an operator logo.
+
+The hand-crafted font currently supports the following characters:
+abcdefghijklmnopqrstuvwxyz and 0123456789
+
+.SH OUTPUT
+The standard `.nol' formatted operator logo is printed to 
+standard output. The header information of that output is taken 
+from \fIgnokii.nol\fR that is distributed with
+.B GNOKII
+-- so you most probably need to give your operator code when uploading it to your
+mobile phone. Use 
+.B gnokii
+or
+.B xlogos
+to do the uploading.
+
+.SH EXAMPLES
+First example writes some todo entries to todo.nol:
+.IP
+\fCtodologo pick up kids - call wife - order chinese >todo.nol\fP
+.LP
+This last example writes some wisdom to the stdout:
+.IP
+\fCtodologo Never underestimate - the power of the dark - side of the force\fP
+.LP
+.SH AUTHOR
+Tomi Ollila <Tomi.Ollila@iki.fi>
+
+.SH COPYING
+This program is distributed under the GNU Public License.
+
+.SH SEE ALSO
+.BR gnokii(1),
+xlogos
+
diff --git a/Docs/man/xgnokii.1x b/Docs/man/xgnokii.1x
new file mode 100644 (file)
index 0000000..5fad541
--- /dev/null
@@ -0,0 +1,39 @@
+.TH "xgnokii" "1x" "May 27, 2001" "" "Gnokii"
+.SH "NAME"
+xgnokii \- graphical interface of the Gnokii telephone toolkit
+.SH "SYNOPSIS"
+.B xgnokii
+
+.SH "DESCRIPTION"
+.PP 
+.B xgnokii
+is a fancy graphical interface for Gnokii.  It has on\-line help and plenty of doo\-dads.
+.PP 
+Gnokii
+is a Linux/Unix tool suite and (eventually) modem/fax driver for Nokia mobile phones, released under the GPL.
+.PP 
+Gnokii
+supports most phones from the 3810/8110 and 5110/6110 series, details including bugs specific to each series appear in the files Docs/README\-3810 and Docs/README\-6110 respectively.  
+
+.SH "DIAGNOSTICS"
+Various error messages are printed to STDERR.
+.SH "BUGS"
+.PP 
+This man page is too short.  The only excuse is that the online help in xgnokii is extensive.  Click and see for yourself.
+
+.SH "AUTHOR"
+Jan Derfinak is the author of the
+.B xgnokii
+.
+
+Hugh Blemings <hugh@blemings.org> and Pavel Janik ml. <Pavel.Janik@suse.cz> are the authors of the
+Gnokii tool suite.
+
+This manual page was written by Erik Rossen <rossen@freesurf.ch>.
+
+See also Docs/CREDITS from the Gnokii sources.
+.SH "COPYING"
+This program is distributed under the GNU Public License.
+
+.SH "SEE ALSO"
+gnokii, gnokiid, xlogos
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..3551ce2
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,54 @@
+
+ TODO: write me!
+
+ For package building see the 'Docs/packaging-howto' file.
+
+0/     If you got an CVS version
+
+       autoconf
+
+1/
+       ./configure [ option ]
+
+       where basic options:
+
+               --prefix=DIR
+                       Install path prefix. Default /usr/local/
+
+               --without-x
+                       Compile without GTK (Xwin) programs
+               
+               --disable-nls
+                       Set if you don't have/want GNU gettext support
+               
+               --enable-security
+                       Set if you want to enable all security features
+
+               --enable-debug
+                       Set if you want debug code in gnokii
+
+               --help          
+                       show all options
+
+
+       Good default (example):
+
+               ./configure --prefix=/usr \
+                           --enable-gettext
+
+2/
+       compilation:
+       
+               make 
+               groupadd gnokii  - it is really needed
+               (make dep - if you want/need) 
+               make install
+               make install-docs
+
+       clean source:
+       
+               make clean       - standard clean
+               make distclean   - clean all ./configure outputs
+                                  (after this command you must run
+                                   ./configure again if you need use
+                                    any Makefile)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..1feaf9c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,208 @@
+
+#
+# $Id$
+#
+# Makefile for the GNOKII tool suite.
+#
+# Copyright (C) 1999 Hugh Blemings & Pavel Janík ml.
+#               2000 Karel Zak
+#
+
+TOPDIR=.
+
+#
+# Makefile.global contains gnokii global settings
+#
+include ${TOPDIR}/Makefile.global
+
+BIN_DIRS = gnokii
+
+ifndef WIN32
+BIN_DIRS += gnokiid
+BIN_DIRS += mgnetd
+BIN_DIRS += mgnetd/mg_demo_client
+endif
+
+DIRS =  common \
+        Docs \
+       $(BIN_DIRS)
+
+#
+# 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
+
+PO_DIR   =     po
+DOCS_DIR =     Docs
+
+all: $(DIRS)
+       @if [ "x$(USE_NLS)" = xyes ]; then \
+               $(MAKE) -C $(PO_DIR); \
+       fi
+
+       @if [ "$(GTK_LIBS)" ]; then \
+               for dir in $(GTK_DIRS); do \
+                   if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir; \
+                   fi; \
+               done \
+       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
+       $(MAKE) -C $@
+
+clean:
+       $(RM) *~ *.orig *.rej include/*~ include/*.orig include/*.rej
+       @for dir in $(DIRS); do \
+           if [ -e $$dir/Makefile ]; then \
+               $(MAKE) -C $$dir clean; \
+           fi; \
+       done
+       @if [ "x$(USE_NLS)" = xyes ]; then \
+               $(MAKE) -C $(PO_DIR) clean; \
+       fi
+
+ifdef OWN_GETOPT
+               $(MAKE) -C getopt clean
+endif
+
+       @if [ "$(GTK_LIBS)" ]; then \
+               for dir in $(GTK_DIRS); do \
+                   if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir clean; \
+                   fi; \
+               done \
+       fi
+
+       $(MAKE) -C Docs clean
+
+       @echo "done"
+
+distclean:     clean
+       @if [ -e $(PO_DIR)/Makefile ]; then \
+               $(MAKE) -C $(PO_DIR) distclean; \
+       fi
+       $(RM) Makefile.global config.cache config.log config.status \
+               include/config.h \
+               include/config.h.in \
+               packaging/RedHat/gnokii.spec \
+               packaging/Slackware/SlackBuild \
+               po/Makefile.in \
+               debian
+
+dep:
+       @for dir in $(DIRS); do \
+           if [ -e $$dir/Makefile ]; then \
+               $(MAKE) -C $$dir dep; \
+           fi; \
+       done
+
+       @if [ "$(GTK_LIBS)" ]; then \
+               for dir in $(GTK_DIRS); do \
+                   if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir dep; \
+                   fi; \
+               done \
+       fi
+       @echo "done"
+
+install:
+       @for dir in $(DIRS); do \
+           if [ -e $$dir/Makefile ]; then \
+               $(MAKE) -C $$dir install; \
+           fi; \
+       done
+       @if [ "x$(USE_NLS)" = xyes ]; then \
+               $(MAKE) -C $(PO_DIR) install; \
+       fi
+
+       @if [ "$(GTK_LIBS)" ]; then \
+               for dir in $(GTK_DIRS); do \
+                   if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir install; \
+                   fi; \
+               done \
+       fi
+       @echo "done"
+
+install-docs:
+       $(MAKE) -C $(DOCS_DIR) install
+       @echo "done"
+
+install-strip:
+       @for dir in $(BIN_DIRS); do \
+               if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir install-strip; \
+               fi; \
+       done
+
+       @if [ "$(GTK_LIBS)" ]; then \
+               @for dir in $(GTK_DIRS); do \
+                       if [ -e $$dir/Makefile ]; then \
+                               $(MAKE) -C $$dir install-strip; \
+                       fi; \
+               done \
+       fi
+       @echo "done"
+
+install-suid:
+       @for dir in $(BIN_DIRS); do \
+               if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir install-suid; \
+               fi; \
+       done
+       @if [ "$(GTK_LIBS)" ]; then \
+               @for dir in $(GTK_DIRS); do \
+                       if [ -e $$dir/Makefile ]; then \
+                               $(MAKE) -C $$dir install-suid; \
+                       fi; \
+               done \
+       fi
+       @echo "done"
+
+install-ss:
+       @for dir in $(BIN_DIRS); do \
+               if [ -e $$dir/Makefile ]; then \
+                       $(MAKE) -C $$dir install-ss; \
+               fi; \
+       done
+
+       @if [ "$(GTK_LIBS)" ]; then \
+               for dir in $(GTK_DIRS); do \
+                       if [ -e $$dir/Makefile ]; then \
+                               $(MAKE) -C $$dir install-ss; \
+                       fi; \
+               done \
+       fi
+       @echo "done"
+
+.PHONY: all install clean distclean dep depend install-docs
diff --git a/Makefile.global.in b/Makefile.global.in
new file mode 100644 (file)
index 0000000..23fa672
--- /dev/null
@@ -0,0 +1,77 @@
+
+#
+# $Id$
+#
+# Global Makefiles configuration for the GNOKII.
+#
+# Copyright (C) 1999 Hugh Blemings & Pavel Janík ml.
+#               2000 Karel Zak
+#
+
+
+
+SHELL          = @SHELL@
+
+LD             = @CC@ -Xlinker -r -nostdlib -nodefaultlibs -o
+
+PACKAGE        = gnokii
+
+srcdir         = @srcdir@
+prefix         = @prefix@
+exec_prefix    = @exec_prefix@
+
+bindir         = @bindir@
+sbindir        = @sbindir@
+xbindir        = ${prefix}/X11R6/bin/
+libdir         = @libdir@
+xgnokii_libdir = @XGNOKIIDIR@/@XPACKAGE@
+man1dir        = @mandir@/man1/
+man8dir        = @mandir@/man8/
+xmandir        = @mandir@/man1/
+docdir         = ${prefix}/doc/${PACKAGE}
+locdir         = @datadir@/locale
+
+BIN_MODE       = 0755
+TEXT_MODE      = 0444
+
+INSTALL        = @INSTALL@
+RM             = @RM@ -f
+FIND           = @FIND@
+MAKE           = @MAKE@
+XGETTEXT       = @XGETTEXT@
+MSGFMT         = @MSGFMT@
+
+CC             = @CC@
+CFLAGS         = @CFLAGS@
+CPPFLAGS       = @CPPFLAGS@
+LIBS           = @LIBS@
+LEX            = @LEX@
+
+GTK_CFLAGS     = @GTK_CFLAGS@
+GTK_LIBS       = @GTK_LIBS@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS   = @PTHREAD_LIBS@
+XPM_LIBS       = @XPM_LIBS@
+XPM_CFLAGS     = @XPM_CFLAGS@
+OWN_GETOPT     = @OWN_GETOPT@
+
+USE_NLS        = @USE_NLS@
+HAVE_XGNOKII   = @HAVE_XGNOKII@
+
+#
+# Global include directory
+#      ($TOPDIR is defined in Makefile)
+#
+GNOKII_INCLUDE = $(TOPDIR)/include
+
+#
+# Win32 option
+#
+ifdef WIN32
+       WIN32 += -I$(TOPDIR)/win32
+endif
+
+
+CFLAGS += -I$(GNOKII_INCLUDE)
+LDFLAGS = $(LIBS) -Wl,--rpath -Wl,$(libdir)
+
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..4735d92
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.3.3_pre8-gold_2002_02_16
diff --git a/common/Makefile b/common/Makefile
new file mode 100644 (file)
index 0000000..e396803
--- /dev/null
@@ -0,0 +1,92 @@
+
+#
+# Makefile for the GNOKII tool suite.
+#
+
+#
+# For this common directory is used "subsystem.o" .o files concept.
+#   (the list of object files to be linked together (to COMMON.o),
+#   and other dirs is used this _one_ file (instead of all OBJS)
+#
+
+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 \
+       gsm-ringtones.o \
+       gsm-coding.o \
+       gsm-datetime.o \
+       gsm-wap.o \
+       gsm-api.o \
+       gsm-phonebook.o \
+       gsm-calendar.o \
+       gsm-networks.o \
+       gsm-bitmaps.o \
+       gsm-sms.o \
+       files/cfgreader.o \
+       misc.o \
+       protocol/fbus.o \
+       protocol/fbusirda.o \
+       protocol/mbus.o \
+       protocol/at.o \
+       newmodules/sniff/sniff.o \
+       newmodules/newat.o \
+       newmodules/n6110.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
+
+ifdef XPM_CFLAGS
+    CFLAGS += $(XPM_CFLAGS)
+endif
+
+all: COMMON.o DATA.o gsm-filetypes.o
+
+COMMON.o: $(OBJS)
+       $(LD) $(LDREL) $(LDOUT) COMMON.o $(OBJS)
+
+DATA.o: $(DATA_OBJS)
+       $(LD) $(LDREL) $(LDOUT) DATA.o $(DATA_OBJS)
+
+gsm-filetypes.o: files/gsm-filetypes.c 
+       $(CC) $(CFLAGS) -c files/gsm-filetypes.c
+
+makelib: $(OBJS) DATA.o gsm-filetypes.o
+       $(CC) -shared -o libmygnokii.so $(OBJS) DATA.o gsm-filetypes.o
+
+clean:
+       $(RM) $(OBJS) $(DATA_OBJS) *~ depend libmygnokii.so COMMON.o gsm-filetypes.o DATA.o
+       
+install:
+       $(INSTALL) -d $(libdir)
+       $(INSTALL) libmygnokii.so $(libdir)
+       @echo
+
+depend dep:
+       $(CC) $(CFLAGS) -MM *.c >depend
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
+
+
+.PHONY: all install clean dep depend
diff --git a/common/data/at-emulator.c b/common/data/at-emulator.c
new file mode 100644 (file)
index 0000000..41445c8
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  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.
+  
+*/
+
+#define                __data_at_emulator_c
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#ifndef WIN32
+
+  #include <termios.h>
+
+#endif
+
+#include "config.h"
+#include "misc.h"
+#include "gsm-common.h"
+#include "gsm-api.h"
+#include "data/at-emulator.h"
+#include "data/virtmodem.h"
+#include "data/datapump.h"
+
+#define MAX_LINE_LENGTH 256
+
+       /* Global variables */
+bool ATEM_Initialised = false; /* Set to true once initialised */
+extern bool    CommandMode;
+extern int ConnectCount;
+
+char ModelName[80]; /* This seems to be needed to avoid seg-faults */
+char PortName[80];
+
+
+       /* Local variables */
+int    PtyRDFD;        /* File descriptor for reading and writing to/from */
+int    PtyWRFD;        /* pty interface - only different in debug mode. */ 
+
+u8     ModemRegisters[MAX_MODEM_REGISTERS];
+char   CmdBuffer[MAX_CMD_BUFFERS][CMD_BUFFER_LENGTH];
+int    CurrentCmdBuffer;
+int    CurrentCmdBufferIndex;
+bool   VerboseResponse;        /* Switch betweek numeric (4) and text responses (ERROR) */
+char    IncomingCallNo;
+int     MessageFormat;          /* Message Format (text or pdu) */
+
+       /* Current command parser */
+void   (*Parser)(char *);
+//void         (*Parser)(char *) = ATEM_ParseAT; /* Current command parser */
+
+GSM_MemoryType         SMSType;
+int    SMSNumber;
+
+       /* If initialised in debug mode, stdin/out is used instead
+          of ptys for interface. */
+bool   ATEM_Initialise(int read_fd, int write_fd, char *model, char *port)
+{
+       PtyRDFD = read_fd;
+       PtyWRFD = write_fd;
+
+       strncpy(ModelName,model,80);
+       strncpy(PortName,port,80);
+
+               /* Initialise command buffer variables */
+       CurrentCmdBuffer = 0;
+       CurrentCmdBufferIndex = 0;
+       
+               /* Default to verbose reponses */
+       VerboseResponse = true;
+
+               /* Initialise registers */
+       ATEM_InitRegisters();
+       
+               /* Initial parser is AT routine */
+       Parser = ATEM_ParseAT;
+       
+               /* Setup defaults for AT*C interpreter. */
+       SMSNumber = 1;
+       SMSType = GMT_ME;
+
+               /* Default message format is PDU */
+       MessageFormat = PDU_MODE;
+
+       /* Set the call passup so that we get notified of incoming calls */
+        GSM->DialData(NULL,-1,&ATEM_CallPassup);
+
+               /* We're ready to roll... */
+       ATEM_Initialised = true;
+       return (true);
+}
+
+       /* Initialise the "registers" used by the virtual modem. */
+void   ATEM_InitRegisters(void) 
+{
+
+       ModemRegisters[REG_RINGATA] = 0;
+       ModemRegisters[REG_RINGCNT] = 2;
+       ModemRegisters[REG_ESC] = '+';
+       ModemRegisters[REG_CR] = 10;
+       ModemRegisters[REG_LF] = 13;
+       ModemRegisters[REG_BS] = 8;
+       ModemRegisters[S35]=7;
+       ModemRegisters[REG_ECHO] = BIT_ECHO;
+
+}
+
+
+/* This gets called to indicate an incoming call */
+
+void ATEM_CallPassup(char c)
+{
+       if ((c >= 0) && (c < 9)) {
+               ATEM_ModemResult(MR_RING);              
+               IncomingCallNo = c;
+       }
+}
+
+
+    /* Handler called when characters received from serial port.
+       calls state machine code to process it. */
+
+void   ATEM_HandleIncomingData(char *buffer, int length)
+{
+       int count;
+       unsigned char out_buf[3];       
+
+       for (count = 0; count < length ; count++) {
+                       /* Echo character if appropriate. */
+               if (ModemRegisters[REG_ECHO] & BIT_ECHO) {
+                       out_buf[0] = buffer[count];
+                       out_buf[1] = 0;
+                       ATEM_StringOut(out_buf);
+               }
+
+                       /* If it's a command terminator character, parse what
+                          we have so far then go to next buffer. */
+               if (buffer[count] == ModemRegisters[REG_CR] ||
+                   buffer[count] == ModemRegisters[REG_LF]) {
+
+                       CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex] = 0x00;
+                       Parser(CmdBuffer[CurrentCmdBuffer]);
+
+                       CurrentCmdBuffer++;
+                       if (CurrentCmdBuffer >= MAX_CMD_BUFFERS) {
+                               CurrentCmdBuffer = 0;
+                       }
+                       CurrentCmdBufferIndex = 0;
+               } else {
+                       CmdBuffer[CurrentCmdBuffer][CurrentCmdBufferIndex] = buffer[count];
+                       CurrentCmdBufferIndex++;
+                       if (CurrentCmdBufferIndex >= CMD_BUFFER_LENGTH) {
+                               CurrentCmdBufferIndex = CMD_BUFFER_LENGTH;
+                       }
+               }
+       }
+}     
+
+
+       /* Parser for standard AT commands.  cmd_buffer must be null terminated. */
+void   ATEM_ParseAT(char *cmd_buffer)
+{
+       char *buf;
+       char number[30];
+
+       if (strncasecmp (cmd_buffer, "AT", 2) != 0) {
+               ATEM_ModemResult(MR_ERROR);
+               return;
+       }
+
+       for (buf = &cmd_buffer[2]; *buf;) {
+               switch (toupper(*buf)) {
+
+               case 'Z':
+                       buf++;
+                       break;
+               case 'A':
+                       buf++;
+                       /* For now we'll also initialise the datapump + rlp code again */
+                       DP_Initialise(PtyRDFD, PtyWRFD);
+                       GSM->DialData(NULL, -1, &DP_CallPassup);
+                       GSM->AnswerCall(IncomingCallNo);
+                       CommandMode = false;
+                       return;
+                       break;
+               case 'D':
+                       /* Dial Data :-) */
+                       /* FIXME - should parse this better */
+                       /* For now we'll also initialise the datapump + rlp code again */
+                       DP_Initialise(PtyRDFD, PtyWRFD);
+                       buf++;
+                       if (toupper(*buf) == 'T') buf++;
+                       if (*buf == ' ') buf++;
+                       strncpy(number, buf, 30);
+                       if (ModemRegisters[S35] == 0) GSM->DialData(number, 1, &DP_CallPassup);
+                       else GSM->DialData(number, 0, &DP_CallPassup);
+                       ATEM_StringOut("\n\r");
+                       CommandMode = false;
+                       return;
+                       break;
+               case 'H':
+                       /* Hang Up */
+                       buf++;
+                       RLP_SetUserRequest(Disc_Req, true);
+                       GSM->CancelCall();
+                       break;
+               case 'S':
+                       /* Change registers - only no. 35 for now */
+                       buf++;
+                       if (memcmp(buf, "35=", 3) == 0) {
+                               buf += 3;
+                               ModemRegisters[S35] = *buf - '0';
+                               buf++;
+                       }
+                       break;
+                 /* E - Turn Echo on/off */
+               case 'E':
+                       buf++;
+                       switch (ATEM_GetNum(&buf)) {
+                       case 0:
+                               ModemRegisters[REG_ECHO] &= ~BIT_ECHO;
+                               break;
+                       case 1:
+                               ModemRegisters[REG_ECHO] |= BIT_ECHO;
+                               break;
+                       default:
+                               ATEM_ModemResult(MR_ERROR);
+                               return;
+                       }
+                       break;
+                       
+                 /* Handle AT* commands (Nokia proprietary I think) */
+               case '*':
+                       buf++;
+                       if (!strcasecmp(buf, "NOKIATEST")) {
+                               ATEM_ModemResult(MR_OK); /* FIXME? */
+                               return;
+                       } else {
+                               if (!strcasecmp(buf, "C")) {
+                                       ATEM_ModemResult(MR_OK);
+                                       Parser = ATEM_ParseSMS;
+                                       return;
+                               }
+                       }
+                       break;
+                       
+                               /* + is the precursor to another set of commands +CSQ, +FCLASS etc. */
+               case '+':
+                       buf++;
+                       switch (toupper(*buf)) {
+                       case 'C':
+                               buf++;
+                               /* Returns true if error occured */
+                               if (ATEM_CommandPlusC(&buf) == true) {
+                                       return; 
+                               }
+                               break;
+                               
+                       case 'G':
+                               buf++;
+                               /* Returns true if error occured */
+                               if (ATEM_CommandPlusG(&buf) == true) {
+                                       return; 
+                               }
+                               break;
+                               
+                       default:
+                               ATEM_ModemResult(MR_ERROR);
+                               return;
+                       }
+                       break;
+                       
+               default: 
+                       ATEM_ModemResult(MR_ERROR);
+                       return;
+               }
+       }
+       
+       ATEM_ModemResult(MR_OK);
+}
+
+static GSM_Error ATEM_ReadSMS(int number, GSM_MemoryType type, GSM_SMSMessage *message)
+{
+       GSM_Error error;
+
+       message->MemoryType = type;
+       message->Location = number;
+       error = GSM->GetSMSMessage(message);
+
+       return error;
+}
+
+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);
+               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>"));
+               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);
+               break;
+       case PDU_MODE:
+               gsprintf(line, MAX_LINE_LENGTH, _("<Not implemented>"));
+               break;
+       default:
+               gsprintf(line, MAX_LINE_LENGTH, _("<Unknown mode>"));
+               break;
+       }
+}
+
+static void ATEM_EraseSMS(int number, GSM_MemoryType type)
+{
+       GSM_SMSMessage message;
+       message.MemoryType = type;
+       message.Location = number;
+       if (GSM->DeleteSMSMessage(&message) == GE_NONE) {
+               ATEM_ModemResult(MR_OK);
+       } else {
+               ATEM_ModemResult(MR_ERROR);
+       }
+}
+
+
+static void ATEM_HandleSMS()
+{
+       GSM_SMSMessage  message;
+       GSM_Error       error;
+       char            buffer[MAX_LINE_LENGTH];
+
+       error = ATEM_ReadSMS(SMSNumber, SMSType, &message);
+       switch (error) {
+       case GE_NONE:
+               ATEM_PrintSMS(buffer, &message, INTERACT_MODE);
+               ATEM_StringOut(buffer);
+               break;
+       default:
+               gsprintf(buffer, MAX_LINE_LENGTH, _("\n\rNo message under number %d\n\r"), SMSNumber);
+               ATEM_StringOut(buffer);
+               break;
+       }
+       return;
+}
+
+       /* Parser for SMS interactive mode */
+void   ATEM_ParseSMS(char *buff)
+{
+       if (!strcasecmp(buff, "HELP")) {
+               ATEM_StringOut(_("\n\rThe following commands work...\n\r"));
+               ATEM_StringOut("DIR\n\r");
+               ATEM_StringOut("EXIT\n\r");
+               ATEM_StringOut("HELP\n\r");
+               return;
+       }
+
+       if (!strcasecmp(buff, "DIR")) {
+               SMSNumber = 1;
+               ATEM_HandleSMS();
+               Parser = ATEM_ParseDIR;
+               return;
+       }
+       if (!strcasecmp(buff, "EXIT")) {
+               Parser = ATEM_ParseAT;
+               ATEM_ModemResult(MR_OK);
+               return;
+       } 
+       ATEM_ModemResult(MR_ERROR);
+}
+
+       /* Parser for DIR sub mode of SMS interactive mode. */
+void   ATEM_ParseDIR(char *buff)
+{
+       switch (toupper(*buff)) {
+               case 'P':
+                       SMSNumber--;
+                       ATEM_HandleSMS();
+                       return;
+               case 'N':
+                       SMSNumber++;
+                       ATEM_HandleSMS();
+                       return;
+               case 'D':
+                       ATEM_EraseSMS(SMSNumber, SMSType);
+                       return;
+               case 'Q':
+                       Parser= ATEM_ParseSMS;
+                       ATEM_ModemResult(MR_OK);
+                       return;
+       }
+       ATEM_ModemResult(MR_ERROR);
+}
+       /* Handle AT+C commands, this is a quick hack together at this
+          stage. */
+bool   ATEM_CommandPlusC(char **buf)
+{
+       float           rflevel;
+       GSM_RFUnits     rfunits = GRF_CSQ;
+       char            buffer[MAX_LINE_LENGTH], buffer2[MAX_LINE_LENGTH];
+       int             status, index;
+       GSM_Error       error;
+       GSM_SMSMessage  message;
+
+       if (strncasecmp(*buf, "SQ", 2) == 0) {
+               buf[0] += 2;
+
+               if (GSM->GetRFLevel(&rfunits, &rflevel) == GE_NONE) {
+                       gsprintf(buffer, MAX_LINE_LENGTH, "\n\r+CSQ: %0.0f, 99", rflevel);
+                       ATEM_StringOut(buffer);
+                       return (false);
+               } else {
+                       return (true);
+               }
+       }
+               /* AT+CGMI is Manufacturer information for the ME (phone) so
+                  it should be Nokia rather than gnokii... */
+       if (strncasecmp(*buf, "GMI", 3) == 0) {
+               buf[0] += 3;
+               ATEM_StringOut(_("\n\rNokia Mobile Phones"));
+               return (false);
+       }
+
+               /* AT+CGSN is IMEI */
+       if (strncasecmp(*buf, "GSN", 3) == 0) {
+               buf[0] += 3;
+               if (GSM->GetIMEI(buffer2) == GE_NONE) {
+                       gsprintf(buffer, MAX_LINE_LENGTH, "\n\r%s", buffer2);
+                       ATEM_StringOut(buffer);
+                       return (false);
+               } else {
+                       return (true);
+               }
+       }
+
+               /* AT+CGMR is Revision (hardware) */
+       if (strncasecmp(*buf, "GMR", 3) == 0) {
+               buf[0] += 3;
+               if (GSM->GetRevision(buffer2) == GE_NONE) {
+                       gsprintf(buffer, MAX_LINE_LENGTH, "\n\r%s", buffer2);
+                       ATEM_StringOut(buffer);
+                       return (false);
+               } else {
+                       return (true);
+               }
+       }
+
+               /* AT+CGMM is Model code  */
+       if (strncasecmp(*buf, "GMM", 3) == 0) {
+               buf[0] += 3;
+               if (GSM->GetModel(buffer2) == GE_NONE) {
+                       gsprintf(buffer, MAX_LINE_LENGTH, "\n\r%s", buffer2);
+                       ATEM_StringOut(buffer);
+                       return (false);
+               } else {
+                       return (true);
+               }
+       }
+
+               /* AT+CMGF is mode selection for message format  */
+       if (strncasecmp(*buf, "MGF", 3) == 0) {
+               buf[0] += 3;
+               switch (**buf) {
+               case '=':
+                       buf[0]++;
+                       switch (**buf) {
+                       case '0':
+                               buf[0]++;
+                               MessageFormat = PDU_MODE;
+                               break;
+                       case '1':
+                               buf[0]++;
+                               MessageFormat = TEXT_MODE;
+                               break;
+                       default:
+                               return (true);
+                       }
+                       break;
+               case '?':
+                       buf[0]++;
+                       gsprintf(buffer, MAX_LINE_LENGTH, "\n\r+CMGF: %d", MessageFormat);
+                       ATEM_StringOut(buffer);
+                       break;
+               default:
+                       return (true);
+               }
+               return (false);
+       }
+
+               /* AT+CMGR is reading a message */
+       if (strncasecmp(*buf, "MGR", 3) == 0) {
+               buf[0] += 3;
+               switch (**buf) {
+               case '=':
+                       buf[0]++;
+                       sscanf(*buf, "%d", &index);
+                       buf[0] += strlen(*buf);
+
+                       error = ATEM_ReadSMS(index, SMSType, &message);
+                       switch (error) {
+                       case GE_NONE:
+                               ATEM_PrintSMS(buffer2, &message, MessageFormat);
+                               gsprintf(buffer, MAX_LINE_LENGTH, "\n\r+CMGR: %s", buffer2);
+                               ATEM_StringOut(buffer);
+                               break;
+                       default:
+                               gsprintf(buffer, MAX_LINE_LENGTH, "\n\r+CMS ERROR: %d\n\r", error);
+                               ATEM_StringOut(buffer);
+                               return (true);
+                       }
+                       break;
+               default:
+                       return (true);
+               }
+               return (false);
+       }
+
+               /* AT+CMGL is listing messages */
+       if (strncasecmp(*buf, "MGL", 3) == 0) {
+               buf[0]+=3;
+               status = -1;
+
+               switch (**buf) {
+               case 0:
+               case '=':
+                       buf[0]++;
+                       /* process <stat> parameter */
+                       if (*(*buf-1) == 0 || /* i.e. no parameter given */
+                               strcasecmp(*buf, "1") == 0 ||
+                               strcasecmp(*buf, "3") == 0 ||
+                               strcasecmp(*buf, "\"REC READ\"") == 0 ||
+                               strcasecmp(*buf, "\"STO SENT\"") == 0) {
+                               status = GSS_SENTREAD;
+                       } else if (strcasecmp(*buf, "0") == 0 ||
+                               strcasecmp(*buf, "2") == 0 ||
+                               strcasecmp(*buf, "\"REC UNREAD\"") == 0 ||
+                               strcasecmp(*buf, "\"STO UNSENT\"") == 0) {
+                               status = GSS_NOTSENTREAD;
+                       } else if (strcasecmp(*buf, "4") == 0 ||
+                               strcasecmp(*buf, "\"ALL\"") == 0) {
+                               status = 4; /* ALL */
+                       } else {
+                               return true;
+                       }
+                       buf[0] += strlen(*buf);
+
+                       /* check all message storages */
+                       for (index = 1; index <= 20; index++) {
+                               error = ATEM_ReadSMS(index, SMSType, &message);
+                               switch (error) {
+                               case GE_NONE:
+                                       /* print messsage if it has the required status */
+                                       if (message.Status == status || status == 4 /* ALL */) {
+                                               ATEM_PrintSMS(buffer2, &message, MessageFormat);
+                                               gsprintf(buffer, MAX_LINE_LENGTH, "\n\r+CMGL: %d,%s", index, buffer2);
+                                               ATEM_StringOut(buffer);
+                                       }
+                                       break;
+                               case GE_EMPTYSMSLOCATION:
+                                       /* don't care if this storage is empty */
+                                       break;
+                               default:
+                                       /* print other error codes and quit */
+                                       gsprintf(buffer, MAX_LINE_LENGTH, "\n\r+CMS ERROR: %d\n\r", error);
+                                       ATEM_StringOut(buffer);
+                                       return (true);
+                               }
+                       }
+                       break;
+               default:
+                       return (true);
+               }
+               return (false);
+       }
+
+       return (true);
+}
+
+       /* AT+G commands.  Some of these responses are a bit tongue in cheek... */
+bool   ATEM_CommandPlusG(char **buf)
+{
+       char            buffer[MAX_LINE_LENGTH];
+
+               /* AT+GMI is Manufacturer information for the TA (Terminal Adaptor) */
+       if (strncasecmp(*buf, "MI", 3) == 0) {
+               buf[0] += 2;
+
+               ATEM_StringOut(_("\n\rHugh Blemings, Pavel Janík ml. and others..."));
+               return (false);
+       }
+
+               /* AT+GMR is Revision information for the TA (Terminal Adaptor) */
+       if (strncasecmp(*buf, "MR", 3) == 0) {
+               buf[0] += 2;
+               gsprintf(buffer, MAX_LINE_LENGTH, "\n\r%s %s %s", VERSION, __TIME__, __DATE__);
+
+               ATEM_StringOut(buffer);
+               return (false);
+       }
+
+               /* AT+GMM is Model information for the TA (Terminal Adaptor) */
+       if (strncasecmp(*buf, "MM", 3) == 0) {
+               buf[0] += 2;
+
+               gsprintf(buffer, MAX_LINE_LENGTH, _("\n\rgnokii configured for %s on %s"), ModelName, PortName);
+               ATEM_StringOut(buffer);
+               return (false);
+       }
+
+               /* AT+GSN is Serial number for the TA (Terminal Adaptor) */
+       if (strncasecmp(*buf, "SN", 3) == 0) {
+               buf[0] += 2;
+
+               gsprintf(buffer, MAX_LINE_LENGTH, _("\n\rnone built in, choose your own"));
+               ATEM_StringOut(buffer);
+               return (false);
+       }
+
+       return (true);
+}
+
+       /* Send a result string back.  There is much work to do here, see
+          the code in the isdn driver for an idea of where it's heading... */
+void   ATEM_ModemResult(int code) 
+{
+       char    buffer[16];
+
+       if (VerboseResponse == false) {
+               sprintf(buffer, "\n\r%d\n\r", code);
+               ATEM_StringOut(buffer);
+       } else {
+               switch (code) {
+                       case MR_OK:     
+                                       ATEM_StringOut("\n\rOK\n\r");
+                                       break;
+
+                       case MR_ERROR:
+                                       ATEM_StringOut("\n\rERROR\n\r");
+                                       break;
+
+                       case MR_CARRIER:
+                                       ATEM_StringOut("\n\rCARRIER\n\r");
+                                       break;
+
+                       case MR_CONNECT:
+                                       ATEM_StringOut("\n\rCONNECT\n\r");
+                                       break;
+
+                       case MR_NOCARRIER:
+                                       ATEM_StringOut("\n\rNO CARRIER\n\r");
+                                       break;
+                       case MR_RING:
+                                       ATEM_StringOut("RING\n\r");
+                                       break;
+                       default:
+                                       ATEM_StringOut(_("\n\rUnknown Result Code!\n\r"));
+                                       break;
+               }
+       }
+
+}
+
+
+       /* Get integer from char-pointer, set pointer to end of number
+          stolen basically verbatim from ISDN code.  */
+int ATEM_GetNum(char **p)
+{
+       int v = -1;
+
+       while (*p[0] >= '0' && *p[0] <= '9') {
+               v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+       }
+
+       return v;
+}
+
+       /* Write string to virtual modem port, either pty or
+          STDOUT as appropriate.  This function is only used during
+          command mode - data pump is used when connected.  */
+void   ATEM_StringOut(char *buffer)
+{
+       int             count = 0;
+       char    out_char;
+
+       while (count < strlen(buffer)) {
+
+                       /* Translate CR/LF/BS as appropriate */
+               switch (buffer[count]) {
+                       case '\r':
+                               out_char = ModemRegisters[REG_CR];
+                               break;
+                       case '\n':
+                               out_char = ModemRegisters[REG_LF];
+                               break;
+                       case '\b':
+                               out_char = ModemRegisters[REG_BS];
+                               break;
+                       default:
+                               out_char = buffer[count];
+                               break;
+               }
+
+               write(PtyWRFD, &out_char, 1);
+               count ++;
+       }
+
+}
diff --git a/common/data/datapump.c b/common/data/datapump.c
new file mode 100644 (file)
index 0000000..4eede53
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  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.
+
+*/
+
+#define                __data_datapump_c
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+
+#include "misc.h"
+#include "gsm-common.h"
+#include "gsm-api.h"
+#include "data/at-emulator.h"
+#include "data/virtmodem.h"
+#include "data/datapump.h"
+#include "data/rlp-common.h"
+
+/* Global variables */
+extern bool CommandMode;
+
+/* Local variables */
+int            PtyRDFD;        /* File descriptor for reading and writing to/from */
+int            PtyWRFD;        /* pty interface - only different in debug mode. */ 
+struct pollfd ufds;
+u8 pluscount;
+bool connected;
+
+bool DP_Initialise(int read_fd, int write_fd)
+{
+       PtyRDFD = read_fd;
+       PtyWRFD = write_fd;
+       ufds.fd=PtyRDFD;
+       ufds.events=POLLIN;
+       RLP_Initialise(GSM->SendRLPFrame, DP_CallBack);
+       RLP_SetUserRequest(Attach_Req,true);
+       pluscount=0;
+       connected=false;
+       return true;
+}
+
+
+int DP_CallBack(RLP_UserInds ind, u8 *buffer, int length)
+{
+       int temp;
+
+       switch(ind) {
+       case Data:
+               if (CommandMode==false) write(PtyWRFD, buffer, length);
+               break;
+       case Conn_Ind:
+               if (CommandMode==false) ATEM_ModemResult(MR_CARRIER);
+               RLP_SetUserRequest(Conn_Req,true);
+               break;
+       case StatusChange:
+               if (buffer[0]==0) {
+                       connected=true;
+                       if (CommandMode==false) ATEM_ModemResult(MR_CONNECT);
+               }
+               break;
+       case Disc_Ind:
+               if (CommandMode==false) ATEM_ModemResult(MR_NOCARRIER);
+               connected=false;
+               /* Set the call passup back to the at emulator */
+               GSM->DialData(NULL,-1,&ATEM_CallPassup);
+               CommandMode=true;
+               break;
+       case Reset_Ind:
+               RLP_SetUserRequest(Reset_Resp,true);
+               break;
+       case GetData:
+               if (poll(&ufds,1,0)) {
+
+                       /* Check if the program has closed */
+                       /* Return to command mode */
+                       /* Note that the call will still be in progress, */
+                       /* as with a normal modem (I think) */
+
+                       if (ufds.revents!=POLLIN) { 
+                               CommandMode=true;
+                               /* Set the call passup back to the at emulator */
+                               GSM->DialData(NULL,-1,&ATEM_CallPassup);
+                               return 0;
+                       }
+
+                       temp = read(PtyRDFD, buffer, length);
+
+                       if (temp<0) return 0; /* FIXME - what do we do now? */
+
+                       /* This will only check +++ and the beginning of a read */
+                       /* But there should be a pause before it anyway */
+      
+                       if (buffer[0]=='+') {
+                               pluscount++;
+                               if (temp>1) {
+                                       if (buffer[1]=='+') pluscount++;
+                                       else pluscount=0;
+                                       if (temp>2) {
+                                               if (buffer[2]=='+') pluscount++;
+                                               else pluscount=0;
+                                               if (temp>3) pluscount=0;
+                                       }
+                               }
+                       } else pluscount=0;
+      
+                       if (pluscount==3) {
+                               CommandMode=true;
+                               /* Set the call passup back to the at emulator */
+                               GSM->DialData(NULL,-1,&ATEM_CallPassup);
+                               ATEM_ModemResult(MR_OK);
+                               break;
+                       }
+      
+                       return temp;
+               }
+               return 0;
+               break;
+
+       default:
+
+       }
+       return 0;
+}
+
+void DP_CallPassup(char c)
+{
+       switch (c) {
+       case 'D':
+               if (CommandMode==false) ATEM_ModemResult(MR_CARRIER);
+               RLP_SetUserRequest(Conn_Req,true);
+               connected=true;
+               break;
+       case ' ':
+               CommandMode=true;
+               /* Set the call passup back to the at emulator */
+               GSM->DialData(NULL,-1,&ATEM_CallPassup);
+               ATEM_ModemResult(MR_NOCARRIER);
+               RLP_SetUserRequest(Disc_Req, true);
+               connected=false;
+               break;
+       default:
+               break;
+       }
+}
diff --git a/common/data/rlp-common.c b/common/data/rlp-common.c
new file mode 100644 (file)
index 0000000..9c46e0d
--- /dev/null
@@ -0,0 +1,2069 @@
+/*
+
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  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
+  the SIM card from SuSE for testing purposes).
+
+  Actual implementation of RLP protocol. Based on GSM 04.22 version 7.1.0,
+  downloadable from www.etsi.org (if you register with them)
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "data/rlp-common.h"
+#include "data/rlp-crc24.h"
+#include "gsm-common.h" /* For GSM error and RLP send function. */
+#include "misc.h" /* For u8, u32 etc. */
+
+#ifdef WIN32
+#define INLINE __inline
+#else
+#define INLINE inline
+#endif
+
+/* Our state machine which handles all of nine possible states of RLP
+   machine. */
+void MAIN_STATE_MACHINE(RLP_F96Frame *frame, RLP_F96Header *header);
+
+/* This is the type we are just handling. */
+RLP_FrameTypes CurrentFrameType;
+
+/* Current state of RLP state machine. */
+RLP_State      CurrentState=RLP_S0; /* We start at ADM and Detached */
+
+/* Next state of RLP state machine. */
+RLP_State      NextState;
+
+/* Pointer to Send function that sends frame to phone. */
+bool      (*RLPSendFunction)(RLP_F96Frame *frame, bool out_dtx);
+
+/* Pointer to Passup function which returns data/inds */
+int      (*RLP_Passup)(RLP_UserInds ind, u8 *buffer, int length);
+
+
+/* State variables - see GSM 04.22, Annex A, section A.1.2 */
+
+RLP_StateVariable UA_State;
+RLP_StateVariable UI_State;
+RLP_StateVariable Ackn_State;
+RLP_StateVariable Poll_State;
+RLP_StateVariable Poll_xchg;
+RLP_StateVariable SABM_State;
+RLP_StateVariable DISC_State;
+RLP_StateVariable DM_State;  /* FIXME - not handled */
+RLP_StateVariable XI_R_State;
+RLP_StateVariable XID_C_State;
+RLP_StateVariable XID_R_State;
+RLP_StateVariable TEST_R_State;
+
+u8 VR=0;
+u8 VA=0;
+u8 VS=0;
+u8 VD=0;
+u8 DISC_Count;
+
+u8 DTX_VR;
+RLP_FrameTypes DTX_SF;
+
+#define RLP_M 62
+
+RLP_Data R[RLP_M];
+RLP_Data S[RLP_M];
+
+RLP_StateVariable SABM_State;
+int SABM_Count;
+
+RLP_UserRequestStore UserRequests;
+
+u8 Poll_Count=0;
+
+/* For now timing is done based on a frame reception rate of 20ms */
+/* Serge has measured it as 18.4ms */
+#define RLP_T_Scaling 2
+
+/* Timers - a value of -1 means not set */
+/* To set, timer is loaded with RLP_Timeout1_Limit/RLP_T_Scaling. */
+/* Each received frame (including NULLS / errors) any >0 timer is decrease */
+
+int T;
+int T_RCVS[RLP_M];
+
+bool UA_FBit=true;
+bool Ackn_FBit=false;
+bool DM_FBit=false;  /* FIXME - not handled */
+bool RRReady=false;
+bool LRReady=true;   /* FIXME - not handled (as if we couldn't keep up with 9600bps :-) */
+bool DISC_PBit=false;
+
+u8 LastStatus=0xff;   /* Last Status byte */
+
+
+/* RLP Parameters. FIXME: Reset these - e.g. when entering state 0 */
+
+u8 RLP_SEND_WS = RLP_M-1;
+u8 RLP_RCV_WS = RLP_M-1;
+u8 RLP_Timeout1_Limit = 55;
+u8 RLP_N2 = 15; /* Maximum number of retransmisions. GSM spec says 6 here, but
+                   Nokia will XID this. */
+u8 RLP_T2=0;
+u8 RLP_VersionNumber=0;
+
+
+
+/****** Externally called functions ********/
+/*******************************************/
+
+
+/* Function to initialise RLP code.  Main purpose for now is
+   to set the address of the RLP send function in the API code. */
+
+void RLP_Initialise(bool (*rlp_send_function)(RLP_F96Frame *frame, bool out_dtx), int (*rlp_passup)(RLP_UserInds ind, u8 *buffer, int length))
+{
+       int i;
+
+       RLPSendFunction = rlp_send_function;
+       RLP_Passup=rlp_passup;
+       UserRequests.Conn_Req=false;
+       UserRequests.Attach_Req=false;
+       UserRequests.Conn_Req_Neg=false;
+       UserRequests.Reset_Resp=false;
+       UserRequests.Disc_Req=false;
+       CurrentState=RLP_S0; 
+       T=-1;
+       for (i=0;i<RLP_M;i++) T_RCVS[i]=-1;
+
+       UA_FBit=true;
+       Ackn_FBit=false;
+       DISC_PBit=false;
+       LastStatus=0xff;
+       Poll_Count=0;
+       VR=0;
+       VA=0;
+       VS=0;
+       VD=0;
+
+       RLP_SEND_WS = RLP_M-1;
+       RLP_RCV_WS = RLP_M-1;
+       RLP_Timeout1_Limit = 55;
+       RLP_N2 = 15; 
+       RLP_T2=0;
+       RLP_VersionNumber=0;
+
+}
+
+/* Set a user event */
+/* Called by user program for now */
+
+void RLP_SetUserRequest(RLP_UserRequests type, bool value) {
+
+       switch (type) {
+       case Conn_Req:
+               UserRequests.Conn_Req=value;
+               break;
+       case Attach_Req:
+               UserRequests.Attach_Req=value;
+               break;
+       case Conn_Req_Neg:
+               UserRequests.Conn_Req_Neg=value;
+               break;
+       case Reset_Resp:
+               UserRequests.Reset_Resp=value;
+               break;
+       case Disc_Req:
+               UserRequests.Disc_Req=value;
+               break;
+       default:
+               break;
+       }
+}
+
+
+
+
+/***** Internal functions **********/
+/***********************************/
+
+
+/* Check whether a user event is set */
+
+bool RLP_GetUserRequest(RLP_UserRequests type) {
+
+       bool result=false, *x;
+
+       switch (type) {
+       case Conn_Req:
+               x=&UserRequests.Conn_Req;
+               break;
+       case Attach_Req:
+               x=&UserRequests.Attach_Req;
+               break;
+       case Conn_Req_Neg:
+               x=&UserRequests.Conn_Req_Neg;
+               break;
+       case Reset_Resp:
+               x=&UserRequests.Reset_Resp;
+               break;
+       case Disc_Req:
+               x=&UserRequests.Disc_Req;
+               break;
+       default:
+               x=&result;
+               break;
+       }
+
+       result=*x;
+
+       if ( *x == true )
+               *x=false;
+
+       return result;
+}
+
+void RLP_SetTimer(int *timer)
+{
+       *timer=(int)(RLP_Timeout1_Limit/RLP_T_Scaling);
+}
+
+
+/* Previous sequence number. */
+static INLINE u8 Decr(u8 x)
+{
+       if (x==0)
+               return (RLP_M-1);
+       else
+               return (x-1);
+}
+
+/* Next sequence number. */
+static INLINE u8 Incr(u8 x)
+{
+       if (x==RLP_M-1)
+               return 0;
+       else
+               return (x+1);
+}
+
+/* Difference between sequence numbers. */
+
+/* FIXME: Not used now, so I have commented it out. PJ
+ * static INLINE u8 Diff(u8 x, u8 y)
+ * {
+ *   int result = x-y;
+ *   return (result >= 0) ? result : result + RLP_M;
+ * }
+*/
+
+/* Check value is within range */
+static bool InWindow(u8 val, u8 lower, u8 upper)
+{
+       /* allow for one level of wrapping round */
+       if (lower>=RLP_M) lower-=RLP_M;
+       if (upper>=RLP_M) upper-=RLP_M;
+       if (val>=RLP_M)   val-=RLP_M;
+
+  /*   .......L*****U....... */
+       if (lower <= upper)
+               return (val >= lower) && (val <= upper);
+
+  /*  ******U.........L***** */
+       return (val <= upper) || (val >= lower);
+}
+
+void RLP_Init_link_vars(void)
+{
+       int i;
+  
+       Ackn_State=_idle;
+       Poll_State=_idle;
+       Poll_Count=0;
+       Poll_xchg=_idle;
+       SABM_State=_idle;
+       DISC_State=_idle;
+       RRReady=true;  /* This seems a bit strange but it's what the spec says... */
+       VA=0;
+       VR=0;
+       VS=0;
+       VD=0;  
+       LastStatus=0xff;
+
+       for(i=0;i<RLP_M;i++) {
+               R[i].State=_idle;
+               S[i].State=_idle;
+       }
+  
+}
+
+
+void RLP_AddRingBufferDataToSlots(void)
+{
+       u8 buffer[24];
+       int size;
+
+       while ((S[VD].State==_idle)
+              && ((size=RLP_Passup(GetData,buffer,24))!=0)) {
+               memset(S[VD].Data,0xff,25);    /* FIXME - this isn't necessary - but makes debugging easier! */
+               if (size>23) {
+                       S[VD].Data[0]=0x1e;
+                       size=24;
+               }
+               else S[VD].Data[0]=size;
+    
+               memcpy(&S[VD].Data[1],buffer,size);
+    
+               if (size!=24) S[VD].Data[size+1]=0x1f;
+
+               S[VD].State=_send;
+               VD=Incr(VD);
+       }  
+}
+
+
+/* FIXME: Remove this after finishing. */
+
+void X(RLP_F96Frame *frame) {
+
+       int i;
+
+       for (i=0; i<30; i++)
+               printf("byte[%2d]: %02x\n", i, *( (u8 *)frame+i));
+   
+}
+
+
+void ResetAllT_RCVS(void)
+{
+
+       int i;
+       for (i=0;i<RLP_M;i++) T_RCVS[i]=-1; 
+}
+
+
+
+/* This function is used for sending RLP frames to the phone. */
+
+void RLP_SendF96Frame(RLP_FrameTypes FrameType,
+                      bool OutCR, bool OutPF,
+                      u8 OutNR, u8 OutNS,
+                      u8 *OutData, u8 OutDTX)
+{
+
+       RLP_F96Frame frame;
+       int i;
+
+       frame.Header[0]=0;
+       frame.Header[1]=0;
+
+#define SetCRBit frame.Header[0]|=0x01;
+#define SetPFBit frame.Header[1]|=0x02;
+
+#define ClearCRBit frame.Header[0]&=(~0x01);
+#define ClearPFBit frame.Header[1]&=(~0x02);
+
+       /* If Command/Response bit is set, set it in the header. */
+       if (OutCR)
+               SetCRBit;
+
+
+       /* If Poll/Final bit is set, set it in the header. */
+       if (OutPF)
+               SetPFBit;
+
+
+       /* If OutData is not specified (ie. is NULL) we want to clear frame.Data
+          array for the user. */
+       if (!OutData) {
+
+               frame.Data[0]=0x00; // 0x1f
+
+               for (i=1; i<25; i++)
+                       frame.Data[i]=0;
+       }
+       else {
+               for (i=0; i<25; i++)
+                       frame.Data[i]=OutData[i];
+       }
+
+#define PackM(x)  frame.Header[1]|=((x)<<2);
+#define PackS(x)  frame.Header[0]|=((x)<<1);
+#define PackNR frame.Header[1]|=(OutNR<<2);
+#define PackNS frame.Header[0]|=(OutNS<<3);frame.Header[1]|=(OutNS>>5);
+
+       switch (FrameType) {
+
+               /* Unnumbered frames. Be careful - some commands are used as commands
+                  only, so we have to set C/R bit later. We should not allow user for
+                  example to send SABM as response because in the spec is: The SABM
+                  encoding is used as command only. */
+
+       case RLPFT_U_SABM:
+
+               frame.Header[0]|=0xf8; /* See page 11 of the GSM 04.22 spec - 0 X X 1 1 1 1 1 */
+               frame.Header[1]|=0x01; /* 1 P/F M1 M2 M3 M4 M5 X */
+
+               SetCRBit; /* The SABM encoding is used as a command only. */
+               SetPFBit; /* It is always used with the P-bit set to "1". */
+
+               PackM(RLPU_SABM);
+
+               break;
+
+       case RLPFT_U_UA:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               ClearCRBit; /* The UA encoding is used as a response only. */
+
+               PackM(RLPU_UA);
+
+               break;
+
+       case RLPFT_U_DISC:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               SetCRBit; /* The DISC encoding is used as a command only. */
+
+               PackM(RLPU_DISC);
+
+               break;
+
+       case RLPFT_U_DM:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               ClearCRBit; /* The DM encoding is used as a response only. */
+
+               PackM(RLPU_DM);
+
+               break;
+
+       case RLPFT_U_NULL:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               PackM(RLPU_NULL);
+
+               break;
+
+       case RLPFT_U_UI:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               PackM(RLPU_UI);
+
+               break;
+
+       case RLPFT_U_XID:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               SetPFBit; /* XID frames are always used with the P/F-bit set to "1". */
+
+               PackM(RLPU_XID);
+
+               break;
+
+       case RLPFT_U_TEST:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               PackM(RLPU_TEST);
+
+               break;
+
+       case RLPFT_U_REMAP:
+
+               frame.Header[0]|=0xf8;
+               frame.Header[1]|=0x01;
+
+               ClearPFBit; /* REMAP frames are always used with P/F-bit set to "0". */
+
+               PackM(RLPU_REMAP);
+
+               break;
+
+       case RLPFT_S_RR:
+
+               frame.Header[0]|=0xf0;  /* See page 11 of the GSM 04.22 spec - 0 X X 1 1 1 1 1 */
+               frame.Header[1]|=0x01; /* 1 P/F ...N(R)... */
+
+               PackNR;
+
+               PackS(RLPS_RR);
+
+               break;
+
+       case RLPFT_S_REJ:
+
+               frame.Header[0]|=0xf0;
+               frame.Header[1]|=0x01;
+
+               PackNR;
+
+               PackS(RLPS_REJ);
+
+               break;
+
+       case RLPFT_S_RNR:
+
+               frame.Header[0]|=0xf0;
+               frame.Header[1]|=0x01;
+
+               PackNR;
+
+               PackS(RLPS_RNR);
+
+               break;
+
+       case RLPFT_S_SREJ:
+
+               frame.Header[0]|=0xf0;
+               frame.Header[1]|=0x01;
+
+               PackNR;
+
+               PackS(RLPS_SREJ);
+
+               break;
+
+       case RLPFT_SI_RR:
+
+               PackNR;
+               PackNS;
+
+               PackS(RLPS_RR);
+
+               break;
+
+       case RLPFT_SI_REJ:
+               PackNR;
+               PackNS;
+
+               PackS(RLPS_REJ);
+
+               break;
+
+       case RLPFT_SI_RNR:
+
+               PackNR;
+               PackNS;
+
+               PackS(RLPS_RNR);
+
+               break;
+
+       case RLPFT_SI_SREJ:
+               PackNR;
+               PackNS;
+
+               PackS(RLPS_SREJ);
+
+               break;
+
+       default:
+               break;
+       }
+
+
+       /* Store FCS in the frame. */
+       RLP_CalculateCRC24Checksum((u8 *)&frame, 27, frame.FCS);
+
+       // X(&frame);
+
+       if (RLPSendFunction)
+               RLPSendFunction(&frame, OutDTX);
+
+}
+
+/* Check_input_PDU in Serge's code. */
+
+void RLP_DisplayF96Frame(RLP_F96Frame *frame)
+{
+       int           count;
+       RLP_F96Header header;
+
+       if (T>=0) T--;
+       for (count=0;count<RLP_M;count++) if (T_RCVS[count]>=0) T_RCVS[count]--;
+
+       CurrentFrameType=RLPFT_BAD;
+
+       if (!frame) {
+               /* no frame provided, drop through to state machine anyway */
+       } else if (RLP_CheckCRC24FCS((u8 *)frame, 30) == true) {
+
+               /* Here we have correct RLP frame so we can parse the field of the header
+                  to out structure. */
+
+               RLP_DecodeF96Header(frame, &header);
+
+               switch (header.Type) {
+
+               case RLPFT_U: /* Unnumbered frames. */
+
+#ifdef RLP_DEBUG
+                       fprintf(stdout, "Unnumbered Frame [$%02x%02x] M=%02x ", frame->Header[0],
+                               frame->Header[1],
+                               header.M);
+#endif
+
+                       switch (header.M) {
+
+                       case RLPU_SABM :
+                               if (header.CR == 0 || header.PF == 0) break;
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "Set Asynchronous Balanced Mode (SABM) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_SABM;
+
+                               break;
+
+                       case RLPU_UA:
+                               if (header.CR == 1) break;
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "Unnumbered Acknowledge (UA) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_UA;
+
+                               break;
+
+                       case RLPU_DISC:
+                               if (header.CR == 0) break;
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "Disconnect (DISC) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_DISC;
+
+                               break;
+
+                       case RLPU_DM:
+                               if (header.CR == 1) break;
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "Disconnected Mode (DM) ");
+#endif
+                               CurrentFrameType=RLPFT_U_DM;
+
+                               break;
+
+                       case RLPU_UI:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "Unnumbered Information (UI) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_UI;
+
+                               break;
+
+                       case RLPU_XID:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "Exchange Information (XID) \n");
+                               RLP_DisplayXID(frame->Data);
+#endif
+
+                               CurrentFrameType=RLPFT_U_XID;
+
+                               break;
+
+                       case RLPU_TEST:
+
+#ifdef DEBUG
+                               fprintf(stdout, "Test (TEST) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_TEST;
+
+                               break;
+
+                       case RLPU_NULL:
+
+#ifdef DEBUG
+                               fprintf(stdout, "Null information (NULL) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_NULL;
+
+                               break;
+
+                       case RLPU_REMAP:
+
+#ifdef DEBUG
+                               fprintf(stdout, "Remap (REMAP) ");
+#endif
+
+                               CurrentFrameType=RLPFT_U_REMAP;
+
+                               break;
+                    
+                       default :
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, _("Unknown!!! "));
+#endif
+
+                               CurrentFrameType=RLPFT_BAD;
+
+                               break;
+
+                       }
+                       break;
+            
+               case RLPFT_S: /* Supervisory frames. */
+
+#ifdef RLP_DEBUG
+                       fprintf(stdout, "Supervisory Frame [$%02x%02x] S=0x%x N(R)=%d ",
+                               frame->Header[0],
+                               frame->Header[1],
+                               header.S,
+                               header.Nr);
+#endif
+
+                       switch (header.S) {
+
+                       case RLPS_RR:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "RR");
+#endif
+
+                               CurrentFrameType=RLPFT_S_RR;
+
+                               break;
+
+                       case RLPS_REJ:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "REJ");
+#endif
+
+                               CurrentFrameType=RLPFT_S_REJ;
+
+                               break;
+
+                       case RLPS_RNR:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "RNR");
+#endif
+
+                               CurrentFrameType=RLPFT_S_RNR;
+
+                               break;
+
+                       case RLPS_SREJ:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "SREJ");
+#endif
+
+                               CurrentFrameType=RLPFT_S_SREJ;
+
+                               break;
+
+                       default:
+
+#ifdef DEBUG
+                               fprintf(stdout, _("BAD"));
+#endif
+
+                               CurrentFrameType=RLPFT_BAD;
+
+                               break;
+
+                       }
+
+                       break;
+            
+               default:
+
+#ifdef RLP_DEBUG
+                       fprintf(stdout, "Info+Supervisory Frame [$%02x%02x] S=0x%x N(S)=%d N(R)=%d ",
+                               frame->Header[0],
+                               frame->Header[1],
+                               header.S,
+                               header.Ns,
+                               header.Nr);
+#endif
+
+                       switch (header.S) {
+
+                       case RLPS_RR:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "RR");
+#endif
+
+                               CurrentFrameType=RLPFT_SI_RR;
+
+                               break;
+
+                       case RLPS_REJ:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "REJ");
+#endif
+
+                               CurrentFrameType=RLPFT_SI_REJ;
+
+                               break;
+
+                       case RLPS_RNR:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "RNR");
+#endif
+
+                               CurrentFrameType=RLPFT_SI_RNR;
+
+                               break;
+
+                       case RLPS_SREJ:
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "SREJ");
+#endif
+
+                               CurrentFrameType=RLPFT_SI_SREJ;
+
+                               break;
+
+                       default:
+
+#ifdef DEBUG
+                               fprintf(stdout, "BAD");
+#endif
+
+                               CurrentFrameType=RLPFT_BAD;
+
+                               break;
+                       }
+
+                       break;
+               }   
+
+#ifdef RLP_DEBUG
+
+               /* Command/Response and Poll/Final bits. */
+
+               fprintf(stdout, " C/R=%d P/F=%d", header.CR, header.PF);
+#endif
+
+#ifdef DEBUG
+
+               /* Information. */
+    
+               if (CurrentFrameType!=RLPFT_U_NULL) {
+
+                       fprintf(stdout, "\n");
+
+                       for (count = 0; count < 25; count ++) {
+
+                               if (isprint(frame->Data[count]))
+                                       fprintf(stdout, "[%02x%c]", frame->Data[count], frame->Data[count]);
+                               else
+                                       fprintf(stdout, "[%02x ]", frame->Data[count]);
+
+                               if (count == 15)
+                                       fprintf(stdout, "\n");
+                       }
+               }
+
+#endif
+#ifdef RLP_DEBUG
+               /* FCS. */
+    
+               fprintf (stdout, " FCS: %02x %02x %02x\n\n", frame->FCS[0],
+                        frame->FCS[1],
+                        frame->FCS[2]);
+
+               fflush(stdout);
+
+#endif
+
+       }
+       else {
+
+               /* RLP Checksum failed - don't we need some statistics about these
+                  failures? Nothing is printed, because in the first stage of connection
+                  there are too many bad RLP frames... */
+
+#ifdef DEBUG
+               fprintf(stdout, _("Frame FCS is bad. Ignoring...\n"));
+#endif
+
+       }
+
+       MAIN_STATE_MACHINE(frame, &header);
+
+       /*
+         Y:= outblock();
+       */
+
+       return;
+}
+
+/* FIXME: real TEST_Handling - we do not handle TEST yet. */
+
+void TEST_Handling() {
+}
+
+
+
+/* FIXME: better XID_handling - but this will answer a XID command. */
+
+bool XID_Handling (RLP_F96Frame *frame, RLP_F96Header *header) {
+  
+       u8 count;
+       u8 type;
+       u8 length;
+
+       if (CurrentFrameType == RLPFT_U_XID) {
+    
+               count=0;
+    
+               while (frame->Data[count] !=0) {
+
+                       type=frame->Data[count] >> 4;
+                       length=frame->Data[count] & 0x0f;
+                       count++;
+      
+                       switch (type) {
+
+                       case 0x01: /* RLP Version Number */
+                               RLP_VersionNumber=frame->Data[count];
+                               count+=length;
+                               break;  
+                       case 0x02: /* Interworking Function (IWF) to Mobile Station (MS) window size */
+                               if (frame->Data[count]>=1 && frame->Data[count]<RLP_M)
+                                       RLP_RCV_WS=frame->Data[count];
+                               count+=length;
+                               break;
+                       case 0x03: /* MS to IWF window size */
+                               if (frame->Data[count]>=1 && frame->Data[count]<RLP_M)
+                                       RLP_SEND_WS=frame->Data[count];
+                               count+=length;
+                               break;
+                       case 0x04: /* Acknowledgement Timer (T1). */
+                               RLP_Timeout1_Limit=frame->Data[count];
+                               count+=length;
+                               break;
+                       case 0x05: /* Retransmission attempts (N2). */
+                               RLP_N2=frame->Data[count];
+                               count+=length;
+                               break;
+                       case 0x06: /* Reply delay (T2). */
+                               RLP_T2=frame->Data[count];
+                               count+=length;
+                               break;
+                       case 0x07: /* Compression - not yet! */
+                               break;
+                       default:
+                               count+=length;
+                               break;
+                       }
+               }
+
+               /* Now reassemble a reply */
+    
+               count=0;
+               memset(frame->Data,0x00,25);  /* Makes debugging easier */
+    
+               /* Version Number - force to 0 for now */
+               RLP_VersionNumber=0;
+               frame->Data[count++]=0x11;
+               frame->Data[count++]=RLP_VersionNumber;
+    
+               /* Window sizes */
+               frame->Data[count++]=0x21;
+               frame->Data[count++]=RLP_RCV_WS;
+               frame->Data[count++]=0x31;
+               frame->Data[count++]=RLP_SEND_WS;
+    
+               /* Acknowledgement Timer (T1). */
+               frame->Data[count++]=0x41;
+               frame->Data[count++]=RLP_Timeout1_Limit;
+
+               /* Retransmission attempts (N2). */
+               frame->Data[count++]=0x51;
+               frame->Data[count++]=RLP_N2;
+
+               /* Reply delay (T2). */
+               frame->Data[count++]=0x61;
+               frame->Data[count++]=RLP_T2;
+
+               XID_R_State = _send;
+
+               return true;
+       }
+
+       return false;
+}
+
+
+bool Send_TXU(RLP_F96Frame *frame, RLP_F96Header *header) {
+
+#ifdef DEBUG
+       //    fprintf(stdout, _("Send_TXU()\n"));
+       //    fprintf(stdout, _("XID_R_State=%d\n"), XID_R_State);
+#endif
+
+       /*
+
+         if (RLP_UserEvent(TEST_R_State)) {
+         RLP_SendF96Frame(RLPFT_U_TEST, false, TEST_R_FBit, 0, 0, TEST_R_Data, false);
+         return true;
+         }
+         else
+
+       */
+
+       if (XID_R_State == _send && frame) {
+               RLP_SendF96Frame(RLPFT_U_XID, false, true, 0, 0, frame->Data, false);
+               XID_R_State = _idle;
+               return true;
+       }
+
+       /*
+
+         else if ((XID_C_State == _send ) && (Poll_xchg == _idle)) {
+         RLP_SendF96Frame(RLPFT_U_XID, true, true, 0, 0, XID_C_Data, false);
+         XID_C_State = _wait;
+         T_XID = 1;
+         Poll_xchg = _wait;
+         return true;
+         } else if (RLP_UserEvent(UI_State)) {
+         RLP_SendF96Frame(RLPFT_U_UI, true, false, 0, 0, NULL, false);
+         return true;
+         }
+
+       */
+
+       return false;
+}
+
+
+/* Deliver data */
+
+void RLP_DeliverAllInSeqIF()
+{
+       int i,j;
+
+       do {
+
+               if ((R[VR].Data[0] & 0xE0)!=LastStatus) {
+                       LastStatus=(R[VR].Data[0] & 0xE0);
+                       RLP_Passup(StatusChange,&LastStatus,0);
+               }
+    
+               j=0;
+               i=R[VR].Data[0] & 0x1f;
+               if (i==0x1e) j=24;
+               if (i<0x18) j=i;
+
+               /* FIXME - should check for more data in the frame */
+    
+               RLP_Passup(Data,R[VR].Data+1,j);
+               R[VR].State=_idle;
+               VR=Incr(VR);
+
+       } while (R[VR].State==_rcvd);
+}
+
+
+/* Mark any missing information frames between VR and Ns*/
+void RLP_MarkMissingIF(u8 Ns)
+{
+       u8 i;
+       for (i=VR; i!=Ns; i=Incr(i)) {
+               if (R[i].State==_idle) R[i].State=_srej;  /* bug in spec, fig A.23 */
+       }
+}
+
+
+/* Information frame handler */
+
+bool RLP_I_Handler(RLP_F96Frame *frame, RLP_F96Header *header)
+{
+
+       if ((header->CR) && (header->PF))
+               return true;
+
+       /* If the window size is 61, a received frame must have a sequence
+          number between VR and VR+60 */
+
+       if (!InWindow(header->Ns,VR,VR+RLP_RCV_WS-1))
+               return true;
+
+       if (header->Ns==VR) {
+               /* This is not in the spec but I think it is necessary */
+               if (R[header->Ns].State==_wait) T_RCVS[header->Ns]=-1;
+               R[VR].State=_rcvd;
+               memcpy(R[VR].Data,frame->Data,25);
+               RLP_DeliverAllInSeqIF();
+               Ackn_State=_send;
+       }
+       else {  /* Out of sequence, cause a SREJ */
+               if (R[header->Ns].State==_wait) T_RCVS[header->Ns]=-1;
+               R[header->Ns].State=_rcvd;
+               memcpy(R[header->Ns].Data,frame->Data,25);
+               RLP_MarkMissingIF(header->Ns);
+       }
+    
+       return false;
+}
+
+
+/* Mark acknowledged send frames */
+
+void RLP_AdvanceVA(u8 Nr)
+{
+       while (VA!=Nr) {
+               S[VA].State=_idle;
+               VA=Incr(VA);
+       }
+}
+
+
+/* Decrease VS back down to Nr since these have not been acknowledged */
+
+void RLP_DecreaseVS(u8 Nr)
+{
+       while (VS!=Nr) {
+               VS=Decr(VS);
+               S[VS].State=_send;
+       }
+}
+
+/* Supervisory frame handling */
+
+void RLP_S_Handler(RLP_F96Frame *frame, RLP_F96Header *header)
+{
+       u8 i;
+
+       if ((header->CR) && (header->PF)) {
+               /* Special exchange (ie. error) - counter? */
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Got Poll command\n");
+#endif
+               Ackn_State=_send;
+               Ackn_FBit=true;
+               for (i=0; i<RLP_M; i++) R[i].State=_idle;
+               ResetAllT_RCVS();
+       }
+       if (Poll_State!=_idle) {
+               if (header->PF==0) return;
+               if ((CurrentFrameType==RLPFT_S_SREJ) || (CurrentFrameType==RLPFT_S_REJ) ||
+                   (CurrentFrameType==RLPFT_SI_SREJ) || (CurrentFrameType==RLPFT_SI_REJ)) return;
+               RLP_DecreaseVS(header->Nr);
+               Poll_State=_idle;
+               Poll_xchg=_idle;
+       }
+       switch (CurrentFrameType){
+    
+       case RLPFT_S_RR:
+       case RLPFT_SI_RR:
+               RLP_AdvanceVA(header->Nr);
+               RRReady=true;
+               break;
+       case RLPFT_S_RNR:
+       case RLPFT_SI_RNR:
+               RLP_AdvanceVA(header->Nr);
+               RRReady=false;
+               break;
+       case RLPFT_S_REJ:
+       case RLPFT_SI_REJ:
+               RLP_AdvanceVA(header->Nr);
+               RRReady=true;
+               RLP_DecreaseVS(header->Nr);
+               break;
+       case RLPFT_S_SREJ:
+       case RLPFT_SI_SREJ:
+               S[header->Nr].State=_send;
+               T=-1;
+               return;
+       default:
+               break;
+       }
+
+       if (VA==VS) T=-1;
+
+}
+
+
+/* Find the first SREJ frame */
+
+bool RLP_SREJSlot(u8 *x)
+{
+       u8 i;
+
+       for (i=Incr(VR); i!=VR; i=Incr(i)) if (R[i].State==_srej) {
+               *x=i;
+               return true;
+       }
+  
+       return false;
+}
+
+
+
+/* Check if any SREJ frames need sending, if not send the next in line */
+
+bool RLP_PrepareDataToTransmit(u8 *p)
+{
+       u8 i;
+
+       for (i=VA; i!=VS; i=Incr(i))
+               if (S[i].State==_send) {
+                       *p=i;
+                       S[i].State=_wait;
+                       return true;
+               }
+       if (S[VS].State!=_send) return false;
+       if (!InWindow(VS,VA,VA+RLP_SEND_WS-1))
+               return false;
+       *p=VS;
+       S[VS].State=_wait;
+       VS=Incr(VS);
+       return true;
+}
+
+
+
+/* Send a SREJ command */
+
+void RLP_SendSREJ(u8 x)
+{
+       u8 k;
+  
+       if ((Poll_xchg==_idle) && (Poll_State==_send)) {
+
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending SREJ with poll\n");
+#endif
+
+               RLP_SendF96Frame(RLPFT_S_SREJ, true, true, x , 0 , NULL, false);    
+               R[x].State=_wait;
+               RLP_SetTimer(&T_RCVS[x]);
+               Poll_Count++;
+               Poll_State=_wait;
+               Poll_xchg=_wait;
+               RLP_SetTimer(&T);
+       }
+       else if (RRReady && RLP_PrepareDataToTransmit(&k)) {
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending SREJ for %d along with frame %d\n",x,k);
+#endif
+               RLP_SendF96Frame(RLPFT_SI_SREJ, true, false, x , k , S[k].Data, false); 
+               R[x].State=_wait;
+               RLP_SetTimer(&T_RCVS[x]);
+               RLP_SetTimer(&T);
+       }
+       else {
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending SREJ for %d\n",x);
+#endif
+               RLP_SendF96Frame(RLPFT_S_SREJ, true, false, x , 0 , NULL, false); 
+               R[x].State=_wait;
+               RLP_SetTimer(&T_RCVS[x]);
+       }
+}
+
+
+/* Send a command */
+
+void RLP_Send_XX_Cmd(RLP_FrameTypes type)
+{
+       u8 k;
+
+       if ((Poll_xchg!=_wait) && (Poll_State==_send)) {
+               RLP_SendF96Frame(type, true, true, VR , 0 , NULL, false);    
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending Comd %x with Poll\n",type);
+#endif
+               Ackn_State=_idle;
+               Poll_Count++;
+               Poll_State=_wait;
+               Poll_xchg=_wait;
+               RLP_SetTimer(&T);
+       }
+       else if (RRReady && RLP_PrepareDataToTransmit(&k)) {
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending Comd %x with frame %d\n",type,k);
+#endif
+               RLP_SendF96Frame(type+4, true, false, VR , k , S[k].Data, false); 
+               Ackn_State=_idle;
+               RLP_SetTimer(&T);
+       }
+       else {
+#ifdef RLP_DEBUG
+               if (type!=9)
+                       fprintf(stdout, "Sending Comd %x\n",type);
+#endif
+               RLP_SendF96Frame(type, true, false, VR , 0 , NULL, false); 
+               Ackn_State=_idle;
+               DTX_SF=type;
+               DTX_VR=VR;   /* As v7.1.0 spec */
+       }
+}
+
+
+/* Send a Response */
+
+void RLP_Send_XX_Resp(RLP_FrameTypes type)
+{
+       u8 k;
+  
+       if (RRReady && RLP_PrepareDataToTransmit(&k)) {
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending Resp %x with frame %d\n",type+4,k);
+#endif
+               RLP_SendF96Frame(type+4, false, true, VR , k , S[k].Data, false); 
+               Ackn_State=_idle;
+               Ackn_FBit=false;
+               RLP_SetTimer(&T);
+       }
+       else {
+#ifdef RLP_DEBUG
+               fprintf(stdout, "Sending Resp %x\n",type);
+#endif
+               RLP_SendF96Frame(type, false, true, VR , 0 , NULL, false); 
+               Ackn_State=_idle;
+               Ackn_FBit=false;
+       }
+}
+
+
+/* Decide which frame to use and send it - currently only used in state 4 */
+
+void RLP_SendData()
+{
+       u8 x;
+
+       if (UA_State==_send) {
+               RLP_SendF96Frame(RLPFT_U_UA, false, UA_FBit, 0 , 0 , NULL, false);    
+               UA_State=_idle;
+       }
+       else if (Ackn_FBit==true) {
+#ifdef RLP_DEBUG
+               printf("About to send Poll resp\n");
+#endif
+               if (LRReady) RLP_Send_XX_Resp(RLPFT_S_RR);
+               else RLP_Send_XX_Resp(RLPFT_S_RNR);
+       }
+       else if (RLP_SREJSlot(&x)) RLP_SendSREJ(x); 
+       else if (LRReady) RLP_Send_XX_Cmd(RLPFT_S_RR);
+       else RLP_Send_XX_Cmd(RLPFT_S_RNR);
+}
+
+void MAIN_STATE_MACHINE(RLP_F96Frame *frame, RLP_F96Header *header) {
+       int i;
+
+       switch (CurrentState) {
+
+               /***** RLP State 0. *****/
+
+               /* ADM and Detached.
+
+                  This is the initial state after power on.
+
+                  As long as the RLP entity is "Detached", DISC(P) and/or SABM at the
+                  lower interface is acted upon by sending DM(P) or DM(1). Any other
+                  stimulus at the lower interface is ignored.
+
+                  This state can be exited only with Attach_Req. */
+
+       case RLP_S0:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 0.\n"));
+#endif
+
+               switch (CurrentFrameType) {
+
+               case RLPFT_U_DISC:
+                       RLP_SendF96Frame(RLPFT_U_DM, false, header->PF, 0, 0, NULL, false);
+                       break;
+
+               case RLPFT_U_SABM:
+                       RLP_SendF96Frame(RLPFT_U_DM, false, true, 0, 0, NULL, false);
+                       break;
+
+               default:
+                       RLP_SendF96Frame(RLPFT_U_NULL, false, false, 0, 0, NULL, false);
+                       if (RLP_GetUserRequest(Attach_Req)) {
+                               NextState=RLP_S1;
+                               UA_State=_idle;
+                       }
+                       break;
+               }
+
+               break;
+  
+               /***** RLP State 1. *****/
+
+               /* ADM and Attached.
+
+                  The RLP entity is ready to established a connection, either by
+                  initiating the connection itself (Conn_Req) or by responding to an
+                  incoming connection request (SABM).
+
+                  Upon receiving a DISC PDU, the handling of the UA response is
+                  initiated. */
+
+       case RLP_S1:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 1.\n"));
+#endif
+
+               if (!XID_Handling(frame, header)) {
+
+                       switch(CurrentFrameType) {
+
+                       case RLPFT_U_TEST:
+                               TEST_Handling();
+                               break;
+
+                       case RLPFT_U_SABM:
+                               RLP_Passup(Conn_Ind,NULL,0);
+                               NextState=RLP_S3;
+                               break;
+
+                       case RLPFT_U_DISC:
+                               UA_State=_send;
+                               UA_FBit=header->PF;
+                               break;
+
+                       case RLPFT_BAD:  /* If we get a bad frame we can still respond with SABM */
+                       default:
+                               if (RLP_GetUserRequest(Conn_Req)) {
+                                       SABM_State=_send;
+                                       SABM_Count=0;
+                                       NextState=RLP_S2;
+                               }
+                               break;
+                       }
+
+               }
+               if (!Send_TXU(frame, header)) {
+      
+                       if (UA_State == _send) {
+                               RLP_SendF96Frame(RLPFT_U_UA, false, UA_FBit, 0, 0, NULL, false);
+                               UA_State=_idle;
+                       }
+                       else
+                               RLP_SendF96Frame(RLPFT_U_NULL, false, false, 0, 0, NULL, false);
+               }
+               break;
+  
+               /***** RLP State 2. *****/
+
+       case RLP_S2:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 2.\n"));
+#endif
+
+               if (!XID_Handling(frame, header)) {
+
+                       switch(CurrentFrameType) {
+
+                       case RLPFT_U_TEST:
+                               TEST_Handling();
+                               break;
+
+                       case RLPFT_U_SABM:
+                               /*
+                                 T=0;
+                                 Conn_Conf=true;
+                                 UA_State=_send;
+                                 UA_FBit=true;
+                                 Init_Link_Vars;
+                                 NextState=4;
+                               */
+                               break;
+
+                       case RLPFT_U_DISC:
+                               /*
+                                 T=0;
+                                 DISC_Ind;
+                                 UA_State=_send;
+                                 UA_FBit=header->PF;
+                                 NextState=RLP_S1;
+                               */
+                               break;
+
+                       case RLPFT_U_UA:
+#ifdef DEBUG
+                               fprintf(stdout, _("UA received in RLP state 2.\n"));
+#endif
+
+                               if (SABM_State == _wait && header->PF) {
+                                       T=-1;
+                                       // Conn_Conf=true;
+                                       // Init_Link_Vars;
+                                       NextState=RLP_S4;
+                               }
+                               break;
+
+                       case RLPFT_U_DM:
+                               if (SABM_State == _wait && header->PF) {
+                                       Poll_xchg=_idle;
+                                       // Conn_Conf_Neg=true;
+                                       NextState=RLP_S1;
+                               }
+                               break;
+
+                       default:
+                               if (T == RLP_Timeout1_Limit) {
+                                       Poll_xchg=_idle;
+                                       if (SABM_Count>RLP_N2)
+                                               NextState=RLP_S8;
+                                       SABM_State=_send;
+                               }
+                               break;
+                       }
+               }
+
+               if (!Send_TXU(frame, header)) {
+
+                       if (SABM_State == _send && Poll_xchg == _idle) {
+                               RLP_SendF96Frame(RLPFT_U_SABM, true, true, 0, 0, NULL, false);
+                               SABM_State=_wait;
+                               SABM_Count++;
+                               Poll_xchg=_wait;
+                               T=1;
+                       } else
+                               RLP_SendF96Frame(RLPFT_U_NULL, false, false, 0, 0, NULL, false);
+               }
+
+               if (RLP_GetUserRequest(Disc_Req)) {
+                       T=-1;
+                       DISC_State=_send;
+                       DISC_Count=0;
+                       DISC_PBit=(Poll_xchg==_idle);
+                       NextState=5;
+               }
+  
+               break;
+
+               /***** RLP State 3. *****/
+
+       case RLP_S3:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 3.\n"));
+#endif
+
+               if (!XID_Handling(frame, header)) {
+    
+                       switch(CurrentFrameType) {
+
+                       case RLPFT_U_TEST:
+                               TEST_Handling();
+                               break;
+       
+                       case RLPFT_U_DISC:
+                               RLP_Passup(Disc_Ind,NULL,0);
+                               UA_State=_send;
+                               UA_FBit=header->PF;
+                               NextState=RLP_S1;
+                               break;
+
+                       default:
+                               if (RLP_GetUserRequest(Conn_Req)) {
+                                       UA_State=_send;
+                                       UA_FBit=true;
+                                       NextState=RLP_S4;
+                                       RLP_Init_link_vars();
+                               } else if (RLP_GetUserRequest(Conn_Req_Neg)) {
+                                       DM_State=_send;  /* FIXME - code to handle DM_State - missing from spec? */
+                                       DM_FBit=true;
+                                       NextState=RLP_S1;
+                               }
+                               break;
+                       }
+               }
+    
+               if (!Send_TXU(frame, header)) {
+      
+                       if (UA_State == _send) {
+                               RLP_SendF96Frame(RLPFT_U_UA, false, UA_FBit, 0, 0, NULL, false);
+                               UA_State=_idle;
+                       }
+                       else
+                               RLP_SendF96Frame(RLPFT_U_NULL, false, false, 0, 0, NULL, false);
+               }
+    
+
+               if (RLP_GetUserRequest(Disc_Req)) {
+                       T=-1;
+                       DISC_State=_send;
+                       DISC_Count=0;
+                       DISC_PBit=(Poll_xchg==_idle);
+                       NextState=5;
+               }
+    
+               break;
+    
+               /***** RLP State 4. *****/
+  
+       case RLP_S4:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 4.\n"));
+#endif
+    
+               if (!XID_Handling(frame, header)) {
+
+                       switch (CurrentFrameType) {
+       
+                       case RLPFT_U_TEST:
+                               TEST_Handling();
+                               break;
+                       case RLPFT_U_DISC:
+                               T=-1;
+                               ResetAllT_RCVS();
+                               RLP_Passup(Disc_Ind,NULL,0);
+                               UA_State=_send;
+                               UA_FBit=header->PF;
+                               NextState=RLP_S1;
+                               break;
+                       case RLPFT_U_SABM:
+                               T=-1;
+                               ResetAllT_RCVS();
+                               RLP_Passup(Reset_Ind,NULL,0);
+                               NextState=RLP_S7;
+                               break;
+                       case RLPFT_S_RR:
+                       case RLPFT_S_RNR:
+                       case RLPFT_S_REJ:
+                       case RLPFT_S_SREJ:
+                               /* Should check here for unsolicited Fbit */
+                               /* Spec says: "Nr must be within the set of not yet
+                                  acknowledged I-frames or it must be the next possible
+                                  frame number." That's VA..VS-1 or VS, i.e. VA..VS */
+                               if (!InWindow(header->Nr,VA,VS))
+                                       break;
+                               RLP_S_Handler(frame,header);
+                               break;
+                       case RLPFT_SI_RR:
+                       case RLPFT_SI_RNR:
+                       case RLPFT_SI_REJ:
+                       case RLPFT_SI_SREJ:
+                               /* Should check here for unsolicited Fbit */
+                               if (!InWindow(header->Nr,VA,VS))
+                                       break;
+                               if (!RLP_I_Handler(frame,header)) RLP_S_Handler(frame,header);
+                               break;
+                       default:
+                               break;
+                       }
+               }    
+    
+               for (i=0;i<RLP_M;i++) if (T_RCVS[i]==0) {
+#ifdef RLP_DEBUG
+                       fprintf(stdout, "T_RCVS[%d] Timeout in State 4\n",i);
+#endif
+                       R[i].State=_srej;
+               }
+               if (T==0) {
+                       T=-1;
+#ifdef RLP_DEBUG
+                       fprintf(stdout, "T Timeout in State 4\n");
+#endif      
+
+                       Poll_xchg=_idle;
+                       if (Poll_State==_idle) {
+                               Poll_State=_send;
+                               Poll_Count=0;
+                       }
+                       else {
+                               if (Poll_Count>RLP_N2) {
+#ifdef RLP_DEBUG
+                                       fprintf(stdout, "N2 Errors in State 4\n");
+#endif
+                               }
+                               Poll_State=_send;
+                               Poll_Count++;
+                       }
+               }
+
+               if (!Send_TXU(frame,header)) {
+                       if (UA_State == _send) {
+                               RLP_SendF96Frame(RLPFT_U_UA, false, UA_FBit, 0, 0, NULL, false);
+                               UA_State=_idle;
+                       }
+                       else  RLP_SendData();
+               }      
+
+
+               /* Load any data from the Send ringbuffer into the send slots */
+               RLP_AddRingBufferDataToSlots();
+
+#ifdef RLP_DEBUG
+               //    if (CurrentFrameType!=RLPFT_BAD) 
+               fprintf(stdout, "VD=%d, VA=%d, VS=%d, VR=%d\n",VD,VA,VS,VR);
+#ifdef RLP_DEBUG_STATE
+               {
+                       int zzz;
+      
+                       if(UA_State!=_idle) printf("[UA_State %d]",UA_State);
+                       if(UI_State!=_idle) printf("[UI_State %d]",UI_State);
+                       if(Ackn_State!=_idle) printf("[Ackn_State %d]",Ackn_State);
+                       if(Poll_State!=_idle) printf("[Poll_State %d]",Poll_State);
+                       if(Poll_xchg!=_idle) printf("[Poll_xchg %d]",Poll_xchg);
+                       if(SABM_State!=_idle) printf("[SABM_State %d]",SABM_State);
+                       if(DISC_State!=_idle) printf("[DISC_State %d]",DISC_State);
+                       if(DM_State!=_idle) printf("[DM_State %d]",DM_State);
+                       if(XI_R_State!=_idle) printf("[XI_R_State %d]",XI_R_State);
+                       if(XID_C_State!=_idle) printf("[XID_C_State %d]",XID_C_State);
+                       if(XID_R_State!=_idle) printf("[XID_R_State %d]",XID_R_State);
+                       if(TEST_R_State!=_idle) printf("[TEST_R_State %d]",TEST_R_State);
+
+                       printf("S: ");
+                       for (zzz=0; zzz<RLP_M; zzz++) printf("%d ",S[zzz].State);
+                       printf("\nR: ");
+                       for (zzz=0; zzz<RLP_M; zzz++) printf("%d ",R[zzz].State);
+                       printf("\nT: %d, T_RCVS: ",T);
+                       for (zzz=0; zzz<RLP_M; zzz++) printf("%d ",T_RCVS[zzz]);
+                       printf("\n");
+               }
+#endif
+#endif    
+
+
+               if (RLP_GetUserRequest(Disc_Req)) {
+                       T=-1;
+                       ResetAllT_RCVS();
+                       DISC_State=_send;
+                       DISC_Count=0;
+                       DISC_PBit=(Poll_xchg==_idle);
+                       NextState=5;
+               }
+
+               break;
+    
+
+               /***** RLP State 5. *****/
+  
+       case RLP_S5:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 5.\n"));
+#endif
+    
+               if (!XID_Handling(frame, header)) {
+    
+                       switch (CurrentFrameType) {
+    
+                       case RLPFT_U_UA:
+                       case RLPFT_U_DM:
+                               if ((DISC_State==_wait) && (DISC_PBit==header->PF)) {
+                                       if (DISC_PBit==true) Poll_xchg=_idle;
+                                       T=-1;
+                                       NextState=1;
+                               }
+                               break;
+                       case RLPFT_U_DISC:
+                               T=-1;
+                               UA_State=_send;
+                               UA_FBit=header->PF;
+                               NextState=1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+    
+               if (!Send_TXU(frame,header)) {
+                       if ((DISC_State!=_wait) && !((DISC_PBit==true) && (Poll_xchg==_wait))) {
+                               RLP_SendF96Frame(RLPFT_U_DISC, true, DISC_PBit, 0, 0, NULL, false);
+                               if (DISC_PBit==true) Poll_xchg=_wait;
+                               DISC_State=_wait;
+                               DISC_Count++;
+                               RLP_SetTimer(&T);
+                       }
+                       else
+                               RLP_SendF96Frame(RLPFT_U_NULL, false, false, 0, 0, NULL, false);
+               }
+               if (T==0) {
+                       if (DISC_PBit==1) Poll_xchg=_idle;
+                       DISC_Count++;
+                       if (DISC_Count>RLP_N2) {
+
+#ifdef RLP_DEBUG
+                               fprintf(stdout, "N2 error in State 5!\n");
+#endif
+      
+                       }
+                       DISC_State=_send;
+               }
+
+               break;
+
+               /***** RLP State 6. *****/
+               /* We should only get here after a Reset_Req which is not yet supported */
+
+       case RLP_S6:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 6 - not yet implemented!\n"));
+#endif
+    
+               if (!XID_Handling(frame, header)) {
+
+                       switch (CurrentFrameType) {
+                       default:
+                               break;
+                       }
+
+               }
+
+               if (!Send_TXU(frame,header)) {
+               }
+
+               if (RLP_GetUserRequest(Disc_Req)) {
+                       T=-1;
+                       DISC_State=_send;
+                       DISC_Count=0;
+                       DISC_PBit=(Poll_xchg==_idle);
+                       NextState=5;
+               }
+
+               break;
+
+
+               /***** RLP State 7. *****/
+  
+       case RLP_S7:
+
+#ifdef DEBUG
+               fprintf(stdout, _("RLP state 7.\n"));
+#endif
+    
+               if (!XID_Handling(frame, header)) {
+
+                       switch (CurrentFrameType) {
+                       case RLPFT_U_DISC:
+                               RLP_Passup(Disc_Ind,NULL,0);
+                               UA_State=_send;
+                               UA_FBit=header->PF;
+                               NextState=RLP_S1;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+               if (RLP_GetUserRequest(Reset_Resp)){
+                       UA_State=_send;
+                       UA_FBit=1;
+                       RLP_Init_link_vars();
+                       NextState=RLP_S4;
+               }
+
+               if (!Send_TXU(frame,header)) {
+                       RLP_SendF96Frame(RLPFT_U_NULL, false, false, 0, 0, NULL, false);
+               }
+
+               if (RLP_GetUserRequest(Disc_Req)) {
+                       T=-1;
+                       DISC_State=_send;
+                       DISC_Count=0;
+                       DISC_PBit=(Poll_xchg==_idle);
+                       NextState=5;
+               }
+    
+               break;
+
+
+       default:
+    
+#ifdef DEBUG
+               fprintf(stdout, _("DEBUG: Unknown RLP state!\n"));
+#endif
+
+               break;
+       }
+
+       CurrentState=NextState;
+
+}
+
+/* Given a pointer to an RLP XID frame, display contents in human readable
+   form.  Note for now only Version 0 and 1 are supported.  Fields can appear
+   in any order and are delimited by a zero type field. This function is the
+   exact implementation of section 5.2.2.6, Exchange Identification, XID of
+   the GSM specification 04.22. */
+
+void RLP_DisplayXID(u8 *frame) 
+{
+
+       int count = 25;  /* Sanity check */
+       u8  type, length;
+    
+       fprintf(stdout, "XID: ");
+
+       while ((*frame !=0) && (count >= 0)) {
+
+               type = *frame >> 4;
+               length = *frame & 0x0f;
+
+               switch (type) {
+
+               case 0x01: /* RLP Version Number, probably 1 for Nokia. */
+
+                       frame += length;
+                       fprintf(stdout, "Ver %d ", *frame);
+                       break;
+                
+               case 0x02: /* IWF to MS window size */
+
+                       frame += length;
+                       fprintf(stdout, "IWF-MS %d ", *frame);
+                       break;
+                
+               case 0x03: /* MS to IWF window size. */
+
+                       frame += length;
+                       fprintf(stdout, "MS-IWF %d ", *frame);
+                       break;
+
+               case 0x04: /* Acknowledgement Timer (T1). */
+
+                       frame += length;
+                       fprintf(stdout, "T1 %dms ", *frame * 10);
+                       break;
+
+               case 0x05: /* Retransmission attempts (N2). */
+
+                       frame += length;
+                       fprintf(stdout, "N2 %d ", *frame);
+                       break;
+
+               case 0x06: /* Reply delay (T2). */
+
+                       frame += length;
+                       fprintf(stdout, "T2 %dms ", *frame * 10);
+                       break;
+
+               case 0x07: /* Compression. */
+
+                       frame ++;
+                       fprintf(stdout, "Comp [Pt=%d ", (*frame >> 4) );
+                       fprintf(stdout, "P0=%d ", (*frame & 0x03) );
+
+                       frame ++;
+                       fprintf(stdout, "P1l=%d ", *frame);
+                       frame ++;
+                       fprintf(stdout, "P1h=%d ", *frame);
+
+                       frame ++;
+                       fprintf(stdout, "P2=%d] ", *frame);
+                       break;
+            
+               default:
+
+                       frame += length;
+                       fprintf(stdout, "Unknown! type=%02x, length=%02x", type, length);
+                       break;
+
+               }
+               count --;
+               frame ++;
+       } 
+
+       return;
+}
+
+/* Given a pointer to an F9.6 Frame, split data out into component parts of
+   header and determine frame type. */
+
+void RLP_DecodeF96Header(RLP_F96Frame *frame, RLP_F96Header *header)
+{
+
+       /* Poll/Final bit. */
+
+       if ((frame->Header[1] & 0x02))
+               header->PF = true;
+       else
+               header->PF = false;
+    
+       /* Command/Response bit. */
+
+       if ((frame->Header[0] & 0x01))
+               header->CR = true;
+       else
+               header->CR = false;
+
+       /* Send Sequence Number. */
+
+       header->Ns = frame->Header[0] >> 3;
+
+       if ((frame->Header[1] & 0x01))
+               header->Ns |= 0x20; /* Most significant bit. */
+
+       /* Determine frame type. See the section 5.2.1 in the GSM 04.22
+          specification. */
+
+       switch (header->Ns) {
+
+       case 0x3f: /* Frames of type U, unnumbered frames. */
+
+               /* U frames have M1, ..., M5 stored in the place of N(R). */
+
+               header->Type = RLPFT_U;
+               header->M = (frame->Header[1] >> 2) & 0x1f;
+               return; /* For U frames, we do not need N(R) and bits S1 and S2. */
+                    
+       case 0x3e: /* Frames of type S, supervisory frames. */
+
+               header->Type = RLPFT_S;
+               break;
+                    
+       default: /* Frames of type I+S, numbered information transfer ans
+                   supervisory frames combined. */
+
+               header->Type = RLPFT_IS;
+               break;
+       }
+
+       /* Receive Sequence Number N(R). */
+       header->Nr = frame->Header[1] >> 2;
+
+       /* Status bits (S1 and S2). */
+       header->S = (frame->Header[0] >> 1) & 0x03;
+
+       return;
+}
+
+
diff --git a/common/data/rlp-crc24.c b/common/data/rlp-crc24.c
new file mode 100644 (file)
index 0000000..cc8af90
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+  CRC24 (aka FCS) implementation in RLP.
+
+*/
+
+#include "data/rlp-crc24.h"
+
+/* CRC-24 table is used for computation of RLP checksum. */
+
+const u32 CRC24_Table[256] = {
+    0x00000000, 0x00D6A776, 0x00F64557, 0x0020E221, 0x00B78115, 0x00612663,
+    0x0041C442, 0x00976334, 0x00340991, 0x00E2AEE7, 0x00C24CC6, 0x0014EBB0,
+    0x00838884, 0x00552FF2, 0x0075CDD3, 0x00A36AA5, 0x00681322, 0x00BEB454,
+    0x009E5675, 0x0048F103, 0x00DF9237, 0x00093541, 0x0029D760, 0x00FF7016,
+    0x005C1AB3, 0x008ABDC5, 0x00AA5FE4, 0x007CF892, 0x00EB9BA6, 0x003D3CD0,
+    0x001DDEF1, 0x00CB7987, 0x00D02644, 0x00068132, 0x00266313, 0x00F0C465,
+    0x0067A751, 0x00B10027, 0x0091E206, 0x00474570, 0x00E42FD5, 0x003288A3,
+    0x00126A82, 0x00C4CDF4, 0x0053AEC0, 0x008509B6, 0x00A5EB97, 0x00734CE1,
+    0x00B83566, 0x006E9210, 0x004E7031, 0x0098D747, 0x000FB473, 0x00D91305,
+    0x00F9F124, 0x002F5652, 0x008C3CF7, 0x005A9B81, 0x007A79A0, 0x00ACDED6,
+    0x003BBDE2, 0x00ED1A94, 0x00CDF8B5, 0x001B5FC3, 0x00FB4733, 0x002DE045,
+    0x000D0264, 0x00DBA512, 0x004CC626, 0x009A6150, 0x00BA8371, 0x006C2407,
+    0x00CF4EA2, 0x0019E9D4, 0x00390BF5, 0x00EFAC83, 0x0078CFB7, 0x00AE68C1,
+    0x008E8AE0, 0x00582D96, 0x00935411, 0x0045F367, 0x00651146, 0x00B3B630,
+    0x0024D504, 0x00F27272, 0x00D29053, 0x00043725, 0x00A75D80, 0x0071FAF6,
+    0x005118D7, 0x0087BFA1, 0x0010DC95, 0x00C67BE3, 0x00E699C2, 0x00303EB4,
+    0x002B6177, 0x00FDC601, 0x00DD2420, 0x000B8356, 0x009CE062, 0x004A4714,
+    0x006AA535, 0x00BC0243, 0x001F68E6, 0x00C9CF90, 0x00E92DB1, 0x003F8AC7,
+    0x00A8E9F3, 0x007E4E85, 0x005EACA4, 0x00880BD2, 0x00437255, 0x0095D523,
+    0x00B53702, 0x00639074, 0x00F4F340, 0x00225436, 0x0002B617, 0x00D41161,
+    0x00777BC4, 0x00A1DCB2, 0x00813E93, 0x005799E5, 0x00C0FAD1, 0x00165DA7,
+    0x0036BF86, 0x00E018F0, 0x00AD85DD, 0x007B22AB, 0x005BC08A, 0x008D67FC,
+    0x001A04C8, 0x00CCA3BE, 0x00EC419F, 0x003AE6E9, 0x00998C4C, 0x004F2B3A,
+    0x006FC91B, 0x00B96E6D, 0x002E0D59, 0x00F8AA2F, 0x00D8480E, 0x000EEF78,
+    0x00C596FF, 0x00133189, 0x0033D3A8, 0x00E574DE, 0x007217EA, 0x00A4B09C,
+    0x008452BD, 0x0052F5CB, 0x00F19F6E, 0x00273818, 0x0007DA39, 0x00D17D4F,
+    0x00461E7B, 0x0090B90D, 0x00B05B2C, 0x0066FC5A, 0x007DA399, 0x00AB04EF,
+    0x008BE6CE, 0x005D41B8, 0x00CA228C, 0x001C85FA, 0x003C67DB, 0x00EAC0AD,
+    0x0049AA08, 0x009F0D7E, 0x00BFEF5F, 0x00694829, 0x00FE2B1D, 0x00288C6B,
+    0x00086E4A, 0x00DEC93C, 0x0015B0BB, 0x00C317CD, 0x00E3F5EC, 0x0035529A,
+    0x00A231AE, 0x007496D8, 0x005474F9, 0x0082D38F, 0x0021B92A, 0x00F71E5C,
+    0x00D7FC7D, 0x00015B0B, 0x0096383F, 0x00409F49, 0x00607D68, 0x00B6DA1E,
+    0x0056C2EE, 0x00806598, 0x00A087B9, 0x007620CF, 0x00E143FB, 0x0037E48D,
+    0x001706AC, 0x00C1A1DA, 0x0062CB7F, 0x00B46C09, 0x00948E28, 0x0042295E,
+    0x00D54A6A, 0x0003ED1C, 0x00230F3D, 0x00F5A84B, 0x003ED1CC, 0x00E876BA,
+    0x00C8949B, 0x001E33ED, 0x008950D9, 0x005FF7AF, 0x007F158E, 0x00A9B2F8,
+    0x000AD85D, 0x00DC7F2B, 0x00FC9D0A, 0x002A3A7C, 0x00BD5948, 0x006BFE3E,
+    0x004B1C1F, 0x009DBB69, 0x0086E4AA, 0x005043DC, 0x0070A1FD, 0x00A6068B,
+    0x003165BF, 0x00E7C2C9, 0x00C720E8, 0x0011879E, 0x00B2ED3B, 0x00644A4D,
+    0x0044A86C, 0x00920F1A, 0x00056C2E, 0x00D3CB58, 0x00F32979, 0x00258E0F,
+    0x00EEF788, 0x003850FE, 0x0018B2DF, 0x00CE15A9, 0x0059769D, 0x008FD1EB,
+    0x00AF33CA, 0x007994BC, 0x00DAFE19, 0x000C596F, 0x002CBB4E, 0x00FA1C38,
+    0x006D7F0C, 0x00BBD87A, 0x009B3A5B, 0x004D9D2D
+};
+
+void    RLP_CalculateCRC24Polinomial(u8 *data, int length, u32 *polinomial)
+{
+
+    int     i;
+    u8      cur;
+
+    *polinomial = 0x00ffffff;
+
+    for (i = 0; i < length; i++) {
+        cur = (*polinomial & 0x0000ffff) ^ data[i];
+        *polinomial = (*polinomial >> 8) ^ CRC24_Table[cur];
+    }
+
+    *polinomial = ((~*polinomial) & 0x00ffffff);
+}
+
+
+void    RLP_CalculateCRC24Checksum(u8 *data, int length, u8 *crc)
+{
+    u32     polinomial;
+
+    RLP_CalculateCRC24Polinomial(data, length, &polinomial);
+    crc[0] = polinomial & 0x0000ffff;
+    crc[1] = (polinomial >> 8) & 0x0000ffff;
+    crc[2] = (polinomial >> 16) & 0x0000ffff;
+
+}
+
+bool    RLP_CheckCRC24FCS(u8 *data, int length)
+{
+
+    u8     crc[] = { 0x00, 0x00, 0x00 };
+
+    RLP_CalculateCRC24Checksum(data, length - 3, crc);
+
+    if (((data[length - 3] == crc[0]) &&
+        (data[length - 2] == crc[1]) &&
+        (data[length - 1] == crc[2]))) {
+        return (true);
+    }
+    return (false);
+}
diff --git a/common/data/virtmodem.c b/common/data/virtmodem.c
new file mode 100644 (file)
index 0000000..7c7f4e2
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  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
+  code in gsm-api.c, at-emulator.c and datapump.c. The code here provides
+  the overall framework and coordinates switching between command mode
+  (AT-emulator) and "online" mode where the data pump code translates data
+  from/to the GSM handset and the modem data/fax stream.
+
+*/
+
+#define                __virtmodem_c
+
+#include <config.h>
+
+/* This is the right way to include stdlib with __USE_XOPEN defined */
+#ifdef USE_UNIX98PTYS
+# define _XOPEN_SOURCE 500
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "misc.h"
+#include "gsm-api.h"
+#include "gsm-common.h"
+#include "data/at-emulator.h"
+#include "data/datapump.h"
+#include "data/virtmodem.h"
+#include "data/rlp-common.h"
+
+       /* Global variables */
+
+//extern bool TerminateThread;
+int ConnectCount;
+
+       /* Local variables */
+
+int            PtyRDFD;        /* File descriptor for reading and writing to/from */
+int            PtyWRFD;        /* pty interface - only different in debug mode. */ 
+
+bool   UseSTDIO;       /* Use STDIO for debugging purposes instead of pty */
+bool   CommandMode;
+
+pthread_t              Thread;
+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)
+{
+       int             rtn;
+
+       CommandMode = true;
+
+       RequestTerminate = false;
+
+       if (debug_mode == true) {
+               UseSTDIO = true;
+       }
+       else {
+               UseSTDIO = false;
+       }
+
+       if (GSMInit) {
+#ifdef DEBUG
+         fprintf (stderr , "Initialising GSM\n");
+#endif /* DEBUG */
+         if ((VM_GSMInitialise(model, port, initlength, connection, synchronizetime) != GE_NONE)) {
+               fprintf (stderr, _("VM_Initialise - VM_GSMInitialise failed!\n"));
+               return (false);
+               
+         }
+       }
+       GSMInit=false;
+
+       if (VM_PtySetup(bindir) < 0) {
+               fprintf (stderr, _("VM_Initialise - VM_PtySetup failed!\n"));
+               return (false);
+       }
+    
+       if (ATEM_Initialise(PtyRDFD, PtyWRFD, model, port) != true) {
+               fprintf (stderr, _("VM_Initialise - ATEM_Initialise failed!\n"));
+               return (false);
+       }
+
+       if (DP_Initialise(PtyRDFD, PtyWRFD) != true) {
+               fprintf (stderr, _("VM_Initialise - DP_Initialise failed!\n"));
+               return (false);
+       }
+
+               /* Create and start thread, */
+       rtn = pthread_create(&Thread, NULL, (void *) VM_ThreadLoop, (void *)NULL);
+
+    if (rtn == EAGAIN || rtn == EINVAL) {
+        return (false);
+    }
+       return (true);
+}
+
+void   VM_ThreadLoop(void)
+{
+       int res;
+       struct pollfd ufds;
+
+               /* Note we can't use signals here as they are already used
+                  in the FBUS code.  This may warrant changing the FBUS
+                  code around sometime to use select instead to free up
+                  the SIGIO handler for mainline code. */
+
+       ufds.fd=PtyRDFD;
+       ufds.events=POLLIN;
+
+       while (!RequestTerminate) {
+         if (!CommandMode) {
+           sleep(1);
+         } else {  /* If we are in data mode, leave it to datapump to get the data */
+
+               res=poll(&ufds,1,500);
+
+               switch (res) {
+                       case 0: /* Timeout */
+                               break;
+
+                       case -1:
+                               perror("VM_ThreadLoop - select");
+                               exit (-1);
+
+                       default:
+                         if (ufds.revents==POLLIN) {
+                           VM_CharHandler();
+                         } else usleep(500); /* Probably the file has been closed */
+                         break;
+               }
+         }
+       }
+       
+}
+
+       /* Application should call VM_Terminate to shut down
+          the virtual modem thread */
+void           VM_Terminate(void)
+{
+     
+       /* Request termination of thread */
+       RequestTerminate = true;
+
+       /* Now wait for thread to terminate. */
+       pthread_join(Thread, NULL);
+
+       if (!UseSTDIO) {
+               close (PtyRDFD);
+               close (PtyWRFD);
+       }
+}
+
+       /* Open pseudo tty interface and (in due course create a symlink
+          to be /dev/gnokii etc. ) */
+
+int            VM_PtySetup(char *bindir)
+{
+       int                     err;
+       char            mgnokiidev[200];
+       char            *slave_name;
+       char            cmdline[200];
+
+       if (bindir) {
+               strncpy(mgnokiidev, bindir, 200);
+               strcat(mgnokiidev, "/");
+       }
+       strncat(mgnokiidev, "mgnokiidev", 200 - strlen(bindir));
+
+       if (UseSTDIO) {
+               PtyRDFD = STDIN_FILENO;
+               PtyWRFD = STDOUT_FILENO;
+               return (0);
+       }
+       
+       PtyRDFD = VM_GetMasterPty(&slave_name);
+       if (PtyRDFD < 0) {
+               fprintf (stderr, _("Couldn't open pty!\n"));
+               return(-1);
+       }
+       PtyWRFD = PtyRDFD;
+
+               /* Check we haven't been installed setuid root for some reason
+                  if so, don't create /dev/gnokii */
+       if (getuid() != geteuid()) {
+               fprintf(stderr, _("gnokiid should not be installed setuid root!\n"));
+               return (0);
+       }
+
+#ifdef DEBUG
+       fprintf (stderr, _("Slave pty is %s, calling %s to create /dev/gnokii.\n"), slave_name, mgnokiidev);
+#endif /* DEBUG */
+
+               /* Create command line, something line ./mkgnokiidev ttyp0 */
+       sprintf(cmdline, "%s %s", mgnokiidev, slave_name);
+
+               /* And use system to call it. */        
+       err = system (cmdline);
+       
+       return (err);
+
+}
+
+    /* Handler called when characters received from serial port.
+       calls state machine code to process it. */
+void    VM_CharHandler(void)
+{
+    unsigned char   buffer[255];
+    int             res;
+
+
+    /* If we are in command mode, get the character, otherwise leave it */
+
+    if (CommandMode && ATEM_Initialised) {
+      
+      res = read(PtyRDFD, buffer, 255);
+      
+      /* A returned value of -1 means something serious has gone wrong - so quit!! */
+      /* Note that file closure etc. should have been dealt with in ThreadLoop */
+      
+      if (res < 0) {   
+//         TerminateThread=true;
+           return;
+      }
+       
+      ATEM_HandleIncomingData(buffer, res);
+    }  
+}     
+
+       /* Initialise GSM interface, returning GSM_Error as appropriate  */
+GSM_Error      VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection, char *synchronizetime)
+{
+       int             count=0;
+       GSM_Error       error;
+
+               /* Initialise the code for the GSM interface. */     
+
+       error = GSM_Initialise(model,port, initlength, connection, RLP_DisplayF96Frame, synchronizetime);
+
+       if (error != GE_NONE) {
+               fprintf(stderr, _("GSM/FBUS init failed! (Unknown model ?). Quitting.\n"));
+       return (error);
+       }
+
+               /* First (and important!) wait for GSM link to be active. We allow 10
+                  seconds... */
+
+       while (count++ < 200 && *GSM_LinkOK == false) {
+       usleep(50000);
+       }
+
+       if (*GSM_LinkOK == false) {
+               fprintf (stderr, _("Hmmm... GSM_LinkOK never went true. Quitting. \n"));
+               return (GE_NOLINK); 
+       }
+
+       return (GE_NONE);
+}
+
+/* VM_GetMasterPty()
+   Takes a double-indirect character pointer in which to put a slave
+   name, and returns an integer file descriptor.  If it returns < 0, an
+   error has occurred.  Otherwise, it has returned the master pty
+   file descriptor, and fills in *name with the name of the
+   corresponding slave pty.  Once the slave pty has been opened,
+   you are responsible to free *name.  Code is from Developling Linux
+   Applications by Troan and Johnson */
+
+
+int    VM_GetMasterPty(char **name) { 
+
+#ifdef USE_UNIX98PTYS
+       int master, err;
+
+       master = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
+       if (master >= 0) {
+               err = grantpt(master);
+               err = err || unlockpt(master);
+               if (!err) {
+                       *name = ptsname(master);
+               } else {
+                       return(-1);
+               }
+       }
+#else /* USE_UNIX98PTYS */
+   int i = 0 , j = 0;
+   /* default to returning error */
+   int master = -1;
+
+   /* create a dummy name to fill in */
+   *name = strdup("/dev/ptyXX");
+
+   /* search for an unused pty */
+   for (i=0; i<16 && master <= 0; i++) {
+      for (j=0; j<16 && master <= 0; j++) {
+         (*name)[8] = "pqrstuvwxyzPQRST"[i];
+         (*name)[9] = "0123456789abcdef"[j];
+         /* open the master pty */
+         if ((master = open(*name, O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0) {
+            if (errno == ENOENT) {
+               /* we are out of pty devices */
+               free (*name);
+               return (master);
+            }
+         }
+      }
+   }
+   if ((master < 0) && (i == 16) && (j == 16)) {
+      /* must have tried every pty unsuccessfully */
+      free (*name);
+      return (master);
+   }
+
+   /* By substituting a letter, we change the master pty
+    * name into the slave pty name.
+    */
+   (*name)[5] = 't';
+
+#endif /* USE_UNIX98PTYS */
+
+   return (master);
+}
+
diff --git a/common/devices/tekram.c b/common/devices/tekram.c
new file mode 100644 (file)
index 0000000..5da6097
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * $Id$
+ *
+ *
+ * G N O K I I
+ *
+ * A Linux/Unix toolset and driver for Nokia mobile phones.
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#ifndef WIN32
+  #include <fcntl.h>
+  #include <sys/ioctl.h>
+  #include <termios.h>
+  #include "devices/unixserial.h"
+#else
+  #include <windows.h>
+  #include "devices/winserial.h"
+#endif
+
+#include "devices/tekram.h"
+
+int tekram_open(__const char *__file) {
+
+  return (serial_open(__file, O_RDWR | O_NOCTTY | O_NONBLOCK));
+}
+
+void tekram_close(int __fd) {
+
+  serial_setdtrrts(__fd, 0, 0);
+
+  serial_close(__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_changespeed(__fd, 9600);
+}
+
+void tekram_changespeed(int __fd, int __speed) {
+
+  unsigned char speedbyte;
+
+
+  switch (__speed) {
+
+  default:
+  case 9600:   speedbyte = TEKRAM_PW | TEKRAM_B9600;   break;
+  case 19200:  speedbyte = TEKRAM_PW | TEKRAM_B19200;  break;
+  case 38400:  speedbyte = TEKRAM_PW | TEKRAM_B38400;  break;
+  case 57600:  speedbyte = TEKRAM_PW | TEKRAM_B57600;  break;
+  case 115200: speedbyte = TEKRAM_PW | TEKRAM_B115200; break;
+
+  }
+
+
+  tekram_reset(__fd);
+
+  serial_setdtrrts(__fd, 1, 0);
+
+  usleep(7);
+
+  serial_write(__fd, &speedbyte, 1);
+
+  usleep(100000);
+
+  serial_setdtrrts(__fd, 1, 1);
+
+
+  serial_changespeed(__fd, __speed);
+}
+
+size_t tekram_read(int __fd, __ptr_t __buf, size_t __nbytes) {
+
+  return (serial_read(__fd, __buf, __nbytes));
+}
+
+size_t tekram_write(int __fd, __const __ptr_t __buf, size_t __n) {
+
+  return (serial_write(__fd, __buf, __n));
+}
+
+int tekram_select(int fd, struct timeval *timeout) {
+
+  fd_set readfds;
+
+  FD_ZERO(&readfds);
+  FD_SET(fd, &readfds);
+
+  return (select(fd + 1, &readfds, NULL, NULL, timeout));
+
+}
diff --git a/common/devices/unixirda.c b/common/devices/unixirda.c
new file mode 100644 (file)
index 0000000..cfbf39a
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * $Id$
+ *
+ * G N O K I I
+ *
+ * A Linux/Unix toolset and driver for Nokia mobile phones.
+ *
+ * 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
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#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
+#endif
+
+#define INFO_LEN               22
+#define DISCOVERY_TIMEOUT      60.0
+#define DISCOVERY_SLEEP                0.4
+
+static char *phone[] = {
+        "Nokia 5210",
+       "Nokia 6210", "Nokia 6250", "Nokia 6310",
+       "Nokia 7110",
+       "Nokia 8210", "Nokia 8310", "Nokia 8850"
+};
+
+double d_time(void)
+{
+       double          time;
+       struct timeval  tv;
+       
+       gettimeofday(&tv, NULL);
+       
+       time = tv.tv_sec + (((double)tv.tv_usec) / 1000000.0);
+       
+       return time;
+}
+
+double d_sleep(double s)
+{
+       double          time;
+       struct timeval  tv1, tv2;
+       
+       gettimeofday(&tv1, NULL);
+       usleep(s * 1000000);
+       gettimeofday(&tv2, NULL);
+       
+       time = tv2.tv_sec - tv1.tv_sec + (((double)(tv2.tv_usec - tv1.tv_usec)) / 1000000.0);
+       
+       return time;
+}
+
+static int irda_discover_device(int fd)
+{
+
+       struct irda_device_list *list;
+       struct irda_device_info *dev;
+       unsigned char           *buf;
+       int                     s, len, i, j;
+       int                     daddr = -1;
+       double                  t1, t2;
+       int phones = sizeof(phone) / sizeof(*phone);
+       
+       len = sizeof(*list) + sizeof(*dev) * 10;        // 10 = max devices in discover
+       buf = malloc(len);
+       list = (struct irda_device_list *)buf;
+       dev = list->dev;
+       
+       t1 = d_time();
+       
+       do {
+               s = len;
+               memset(buf, 0, s);
+               
+               if (getsockopt(fd, SOL_IRLMP, IRLMP_ENUMDEVICES, buf, &s) == 0) {
+                       for (i = 0; (i < list->len) && (daddr == -1); i++) {
+                               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
+                                       }
+                               }
+                               if (daddr == -1) {
+#ifdef DEBUG
+                                       fprintf(stdout,_("unknown: %s\t%x\n"), dev[i].info, dev[i].daddr);
+#endif
+                               }
+                       }
+               }
+               
+               if (daddr == -1) {
+                       d_sleep(DISCOVERY_SLEEP);
+               }
+               
+               t2 = d_time();
+               
+       } while ((t2 - t1 < DISCOVERY_TIMEOUT) && (daddr == -1));
+       
+       free(buf);
+       
+       return daddr;
+}
+
+int irda_open(void)
+{
+       struct sockaddr_irda    peer;
+       int                     fd = -1, daddr;
+       int                     pgrp;         
+       
+       
+       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 (connect(fd, (struct sockaddr *)&peer, sizeof(peer))) {      /* Connect to service "Nokia:PhoNet" */
+               perror("connect");
+               close(fd);
+               fd = -1;
+               }
+       
+       return fd;
+}
+
+int irda_close(int fd)
+{
+       shutdown(fd, 0);
+       return close(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);
+}
+
+int irda_read(int __fd, __ptr_t __bytes, int __size)
+{
+       return (recv(__fd, __bytes, __size, 0));
+}
+
+int irda_select(int fd, struct timeval *timeout)
+{
+       fd_set readfds;
+
+       FD_ZERO(&readfds);
+       FD_SET(fd, &readfds);
+
+       return (select(fd + 1, &readfds, NULL, NULL, timeout));
+}
diff --git a/common/devices/unixserial.c b/common/devices/unixserial.c
new file mode 100644 (file)
index 0000000..bc7e37e
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+  
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+*/
+
+#include "misc.h"
+
+/* Do not compile this file under Win32 systems. */
+
+#ifndef WIN32
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#if __unices__
+#  include <sys/file.h>
+#endif
+
+#include <termios.h>
+#include "devices/unixserial.h"
+
+#ifdef HAVE_SYS_IOCTL_COMPAT_H
+  #include <sys/ioctl_compat.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* If the target operating system does not have cfsetspeed, we can emulate
+   it. */
+
+#ifndef HAVE_CFSETSPEED
+  #if defined(HAVE_CFSETISPEED) && defined(HAVE_CFSETOSPEED)
+     #define cfsetspeed(t, speed) \
+     (cfsetispeed(t, speed) || cfsetospeed(t, speed))
+  #else
+    static int cfsetspeed(struct termios *t, int speed) {
+    #ifdef HAVE_TERMIOS_CSPEED
+      t->c_ispeed = speed;
+      t->c_ospeed = speed;
+    #else
+      t->c_cflag |= speed;
+    #endif
+      return 0;
+    }
+  #endif
+#endif
+
+#ifndef O_NONBLOCK
+  #define O_NONBLOCK  0
+#endif
+
+/* Structure to backup the setting of the terminal. */
+
+struct termios serial_termios;
+
+/* Open the serial port and store the settings. */
+
+int serial_open(__const char *__file, int __oflag) {
+
+  int __fd;
+  int retcode;
+
+  __fd = open(__file, __oflag);
+  if (__fd == -1) {
+    perror("Gnokii serial_open: open");
+    return (-1);
+  }
+
+  retcode=tcgetattr(__fd, &serial_termios);
+  if(retcode==-1) {
+    perror("Gnokii serial_open:tcgetattr");
+    /* Don't call serial_close since serial_termios is not valid */
+    close(__fd);
+    return(-1);
+  }
+  
+  return __fd;
+}
+
+/* Close the serial port and restore old settings. */
+
+int serial_close(int __fd) {
+
+  if (__fd >= 0)
+    tcsetattr(__fd, TCSANOW, &serial_termios);
+
+  return (close(__fd));
+}
+
+/* Open a device with standard options. */
+
+int serial_opendevice(__const char *__file, int __with_odd_parity, int __with_async, int __with_hw_handshake) {
+
+  int fd;
+  int retcode;
+  struct termios tp;
+
+  /* Open device */
+
+  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;
+  if (__with_odd_parity) {
+    tp.c_cflag |= (PARENB | PARODD);
+    tp.c_iflag = 0;
+  }
+  else
+    tp.c_iflag = IGNPAR;
+  if (__with_hw_handshake)
+    tp.c_cflag |= CRTSCTS;
+  else
+    tp.c_cflag &= ~CRTSCTS;
+
+  tp.c_oflag = 0;
+  tp.c_lflag = 0;
+  tp.c_cc[VMIN] = 1;
+  tp.c_cc[VTIME] = 0;
+
+  retcode=tcflush(fd, TCIFLUSH);
+  if (retcode == -1) {
+    perror("Gnokii serial_opendevice: tcflush");
+    serial_close(fd);
+    return(-1);
+  }
+
+  retcode=tcsetattr(fd, TCSANOW, &tp);
+  if (retcode == -1){
+    perror("Gnokii serial_opendevice: tcsetattr");
+    serial_close(fd);
+    return(-1);
+  }
+
+  return fd;
+}
+
+/* Set the DTR and RTS bit of the serial device. */
+
+void serial_setdtrrts(int __fd, int __dtr, int __rts) {
+
+  unsigned int flags;
+
+  flags = TIOCM_DTR;
+
+  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);
+}
+
+
+int serial_select(int fd, struct timeval *timeout) {
+
+  fd_set readfds;
+
+  FD_ZERO(&readfds);
+  FD_SET(fd, &readfds);
+
+  return (select(fd + 1, &readfds, NULL, NULL, timeout));
+
+}
+
+
+/* Change the speed of the serial device. */
+
+void serial_changespeed(int __fd, int __speed) {
+
+#ifndef SGTTY
+  struct termios t;
+#else
+  struct sgttyb t;
+#endif
+
+  int speed=B9600;
+
+  switch (__speed) {
+    case 9600:   speed = B9600;   break;
+    case 19200:  speed = B19200;  break;
+    case 38400:  speed = B38400;  break;
+    case 57600:  speed = B57600;  break;
+    case 115200: speed = B115200; break;
+  }
+
+#ifndef SGTTY
+  tcgetattr(__fd, &t);
+
+  // 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
+
+  tcsetattr(__fd, TCSADRAIN, &t);
+#else
+  ioctl(__fd, TIOCGETP, &t);
+
+  t.sg_ispeed = speed;
+  t.sg_ospeed = speed;
+
+  ioctl(__fd, TIOCSETN, &t);
+#endif
+}
+
+/* Read from serial device. */
+
+size_t serial_read(int __fd, __ptr_t __buf, size_t __nbytes) {
+
+  return (read(__fd, __buf, __nbytes));
+}
+
+/* Write to serial device. */
+
+size_t serial_write(int __fd, __const __ptr_t __buf, size_t __n) {
+       
+       return (write(__fd, __buf, __n));
+}
+
+#endif  /* WIN32 */
diff --git a/common/gsm-api.c b/common/gsm-api.c
new file mode 100644 (file)
index 0000000..99b73d7
--- /dev/null
@@ -0,0 +1,792 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  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
+  possible hiding the model specific details.
+
+  The underlying code should run in it's own thread to allow communications to
+  the phone to be run independantly of mailing code that calls these API
+  functions.
+
+  Unless otherwise noted, all functions herein block until they complete.  The
+  functions themselves are defined in a structure in gsm-common.h.
+
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#ifdef WIN32
+  #include <windows.h>
+  #include "misc_win32.h"
+#endif
+
+#include "gsm-api.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/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;
+
+/* Define pointer to the GSM_Functions structure used by external code to call
+   relevant API functions. This structure is defined in gsm-common.h. */
+
+GSM_Functions *GSM;
+
+/* Define pointer to the GSM_Information structure used by external code to
+   obtain information that varies from model to model. This structure is also
+   defined in gsm-common.h */
+
+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;
+
+GSM_SpeedDial      *CurrentSpeedDialEntry;
+GSM_Error          CurrentSpeedDialError;
+
+unsigned char      Current_IMEI[GSM_MAX_IMEI_LENGTH];
+unsigned char      Current_Revision[GSM_MAX_REVISION_LENGTH];
+unsigned char      Current_Model[GSM_MAX_MODEL_LENGTH];
+
+GSM_SMSMessage     *CurrentSMSMessage;
+GSM_Error          CurrentSMSMessageError;
+int                CurrentSMSPointer;
+
+GSM_SMSFolders      *CurrentSMSFolders;
+GSM_Error          CurrentSMSFoldersError;
+int                CurrentSMSFoldersCount;
+
+GSM_OneSMSFolder   CurrentSMSFolder;
+GSM_Error          CurrentSMSFolderError;
+int                CurrentSMSFolderID;
+
+GSM_MemoryStatus   *CurrentMemoryStatus;
+GSM_Error          CurrentMemoryStatusError;
+
+GSM_NetworkInfo    *CurrentNetworkInfo;
+GSM_Error          CurrentNetworkInfoError;
+
+GSM_SMSStatus      *CurrentSMSStatus;
+GSM_Error          CurrentSMSStatusError;
+
+GSM_MessageCenter  *CurrentMessageCenter;
+GSM_Error          CurrentMessageCenterError;
+
+int                *CurrentSecurityCodeStatus;
+GSM_Error          CurrentSecurityCodeError;
+GSM_SecurityCode   *CurrentSecurityCode;
+
+GSM_DateTime       *CurrentDateTime;
+GSM_Error          CurrentDateTimeError;
+
+GSM_DateTime       *CurrentAlarm;
+GSM_Error          CurrentAlarmError;
+
+GSM_CalendarNote   *CurrentCalendarNote;
+GSM_Error          CurrentCalendarNoteError;
+
+GSM_NotesInfo      CurrentCalendarNotesInfo,*CurrentCalendarNotesInfo2;
+GSM_Error          CurrentCalendarNotesInfoError;
+
+GSM_Error          CurrentSetDateTimeError;
+GSM_Error          CurrentSetAlarmError;
+
+GSM_Error          CurrentEnableExtendedCommandsError;
+
+int                CurrentRFLevel,
+                   CurrentBatteryLevel,
+                   CurrentPowerSource;
+
+int                CurrentDisplayStatus;
+GSM_Error          CurrentDisplayStatusError;
+
+GSM_Error          CurrentResetPhoneSettingsError;
+
+char               *CurrentNetmonitor;
+GSM_Error          CurrentNetmonitorError;
+
+GSM_Bitmap         *CurrentGetBitmap=NULL;
+GSM_Error          CurrentGetBitmapError;
+
+GSM_Error          CurrentSetBitmapError;
+
+GSM_Error          CurrentSendDTMFError;
+
+GSM_Profile        *CurrentProfile;
+GSM_Error          CurrentProfileError;
+
+GSM_Error          CurrentDisplayOutputError;
+
+GSM_CBMessage      *CurrentCBMessage;
+GSM_Error          CurrentCBError;
+
+int                CurrentPressKeyEvent;
+GSM_Error          CurrentPressKeyError;
+
+GSM_Error          CurrentPlayToneError=GE_UNKNOWN;
+
+GSM_Error          CurrentDialVoiceError;
+
+GSM_Error          CurrentGetOperatorNameError;
+GSM_Network        *CurrentGetOperatorNameNetwork;
+GSM_Error          CurrentSetOperatorNameError;
+
+GSM_Error          CurrentGetIMEIError;
+
+GSM_Error          CurrentGetHWError;
+
+unsigned char      CurrentPPS[4];
+GSM_Error          CurrentProductProfileSettingsError;
+
+char               CurrentIncomingCall[20];
+
+GSM_Error          CurrentBinRingtoneError;
+GSM_BinRingtone    *CurrentGetBinRingtone=NULL;
+
+GSM_Error          CurrentRingtoneError;
+
+GSM_Error          CurrentMagicError;
+
+GSM_Error          CurrentSimlockInfoError;
+GSM_AllSimlocks    *CurrentSimLock;
+
+GSM_Error          CurrentGetWAPBookmarkError;
+GSM_Error          CurrentSetWAPBookmarkError;
+GSM_WAPBookmark    *WAPBookmark;
+
+GSM_Error          CurrentGetWAPSettingsError;
+GSM_WAPSettings    *WAPSettings;
+
+GSM_Error          CurrentCallDivertError;
+GSM_CallDivert    *CurrentCallDivert;
+
+char              *CurrentManufacturer;
+
+/* This is the connection type used in gnokii. */
+GSM_ConnectionType CurrentConnectionType;
+
+/* Pointer to a callback function used to return changes to a calls status */
+/* This saves unreliable polling */
+void (*CurrentCallPassup)(char c);
+
+/* Pointer to callback function in user code to be called when RLP frames
+   are received. */
+void (*CurrentRLP_RXCallback)(RLP_F96Frame *frame);
+
+/* Used to disconnect the call */
+u8 CurrentCallSequenceNumber;
+
+bool CurrentLinkOK;
+
+bool CurrentRequestTerminate;
+
+bool CurrentDisableKeepAlive;
+
+bool CheckModel (GSM_Information InfoToCheck, char *model, GSM_ConnectionType connection) {
+
+  bool found_match=false;
+
+  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;
+  }
+
+  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;
+
+  /* Hopefully is 64 larger as FB38_MAX* / N6110_MAX* */
+  char phonemodel[64];
+
+  if (Protocol->Initialise(device,initlength,connection,rlp_callback)!=GE_NONE)
+  {
+    return GE_NOTSUPPORTED; 
+  }
+
+  if (connection!=GCT_MBUS) {
+    InitLength = atoi(initlength);
+
+    if ((strcmp(initlength, "default") == 0) || (InitLength == 0)) {
+      InitLength = 250;        /* This is the usual value, lower may work. */
+    }
+
+#ifdef DEBUG
+    fprintf(stdout,_("Writing init chars...."));
+#endif
+
+    /* 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);
+    }
+
+#ifdef DEBUG
+    fprintf(stdout,_("Done\n"));  
+#endif
+
+    N6110_SendStatusRequest();
+  }
+
+  usleep(100);
+
+  if (N6110_SendIDFrame()!=GE_NONE)
+    return GE_TIMEOUT;
+  while (N6110_GetModel(phonemodel) != GE_NONE)
+    sleep(1);
+
+  if (!strcmp(phonemodel,"NPE-3") || !strcmp(phonemodel,"NSE-5"))
+  {
+    GSM->Terminate();      
+    
+    /* Set pointers to relevant addresses */
+    GSM = &N7110_Functions;
+    GSM_Info = &N7110_Information;
+    GSM_LinkOK = &CurrentLinkOK;
+    return GE_NONE;
+  }
+
+  return GE_NONE;
+}
+
+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;
+
+  struct tm *now;
+  time_t nowh;
+  GSM_DateTime Date;
+  GSM_Error error;
+  
+  connection2=connection;
+
+  CurrentRLP_RXCallback = rlp_callback;
+  CurrentCallPassup=NULL;
+  CurrentPhonebookEntry=NULL;
+  CurrentNetworkInfo = NULL;
+  CurrentGetBitmap=NULL;
+  CurrentPlayToneError=GE_UNKNOWN;
+  strcpy(CurrentIncomingCall," ");
+  CurrentGetBinRingtone=NULL;
+  CurrentNetworkInfo=NULL;
+  CurrentRequestTerminate=false;
+  CurrentDisableKeepAlive=false;
+  CurrentCalendarNotesInfo.HowMany=2000;
+  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 = &FBUS_Functions;
+    CurrentConnectionType=GCT_FBUS;    
+    connection2=GCT_FBUS;
+    if (TryNewNokia(model,device,initlength,CurrentConnectionType,rlp_callback)==GE_NONE)
+    {
+      found_match=true;
+    } else {
+      GSM->Terminate();      
+    }
+
+    if (!found_match) {
+      usleep(100);          
+      
+      /* For now */
+      GSM = &N6110_Functions;
+      GSM_Info = &N6110_Information;
+      GSM_LinkOK = &CurrentLinkOK;
+#ifdef DEBUG
+      fprintf(stdout,_("Trying DLR3 for new Nokia phones...\n"));
+#endif
+      /* Trying DLR3 */
+      Protocol = &FBUS_Functions;
+      CurrentConnectionType=GCT_DLR3;    
+      connection2=GCT_DLR3;
+      if (TryNewNokia(model,device,initlength,CurrentConnectionType,rlp_callback)==GE_NONE)
+      {
+        found_match=true;
+      } else {
+        GSM->Terminate();      
+      }
+    }
+    
+    if (!found_match) {
+      usleep(100);          
+      
+      /* For now */
+      GSM = &N6110_Functions;
+      GSM_Info = &N6110_Information;
+      GSM_LinkOK = &CurrentLinkOK;
+#ifdef DEBUG
+      fprintf(stdout,_("Trying MBUS for new Nokia phones...\n"));
+#endif
+      /* Trying MBUS */
+      Protocol = &MBUS_Functions;
+      CurrentConnectionType=GCT_MBUS;    
+      connection2=GCT_MBUS;
+      if (TryNewNokia(model,device,initlength,CurrentConnectionType,rlp_callback)==GE_NONE)
+      {
+        found_match=true;
+      } else {
+        GSM->Terminate();      
+      }
+    }
+
+    if (!found_match) return GE_NOTSUPPORTED;
+
+    usleep(50);
+        
+  } else {
+#ifdef DEBUG
+    if (CheckModel (Nsniff_Information, model, connection)) {
+      /* Set pointers to relevant addresses */
+      GSM = &Nsniff_Functions;
+      GSM_Info = &Nsniff_Information;
+      GSM_LinkOK = &CurrentLinkOK;
+      found_match=true;
+    }
+#endif
+
+    if (CheckModel (N6110_Information, model, connection)) {
+      /* Set pointers to relevant addresses */
+      GSM = &N6110_Functions;
+      GSM_Info = &N6110_Information;
+      GSM_LinkOK = &CurrentLinkOK;
+      found_match=true;
+    }
+    if (CheckModel (N7110_Information, model, connection)) {
+      /* Set pointers to relevant addresses */
+      GSM = &N7110_Functions;
+      GSM_Info = &N7110_Information;
+      GSM_LinkOK = &CurrentLinkOK;
+      found_match=true;
+    }
+    if (CheckModel (Nat_Information, model, connection)) {
+      /* Set pointers to relevant addresses */
+      GSM = &Nat_Functions;
+      GSM_Info = &Nat_Information;
+      GSM_LinkOK = &CurrentLinkOK;
+      found_match=true;
+    }
+
+    if (found_match) {
+      switch (connection) {
+        case GCT_FBUS    : Protocol = &FBUS_Functions;    break;
+        case GCT_Infrared: Protocol = &FBUS_Functions;    break;
+        case GCT_Tekram  : Protocol = &FBUS_Functions;    break;
+        case GCT_DLR3    : Protocol = &FBUS_Functions;    break;
+        case GCT_MBUS    : Protocol = &MBUS_Functions;    break;
+        case GCT_Irda    : Protocol = &FBUSIRDA_Functions;break;
+        case GCT_AT      : Protocol = &AT_Functions;      break;
+        case GCT_Default : Protocol = NULL;               break;
+      }
+    } else
+      return GE_NOTSUPPORTED;
+  }
+
+    
+  /* Now call model specific initialisation code. */
+  error=(GSM->Initialise(device, initlength, connection2, rlp_callback));
+
+  /* RTH: FIXME: second try for Irda (6210 only?)*/
+  if ( error!=GE_NONE && connection == GCT_Irda)
+  {
+   #ifdef DEBUG
+     fprintf(stdout,"Irda connection: second try!\n");
+   #endif
+   device_close();
+   error=(GSM->Initialise(device, initlength, connection2, rlp_callback));
+  }
+
+  if (error==GE_NONE && !strcmp(SynchronizeTime,"yes"))
+  {
+    nowh=time(NULL);
+    now=localtime(&nowh);
+
+    Date.Year = now->tm_year;
+    Date.Month = now->tm_mon+1;
+    Date.Day = now->tm_mday;
+    Date.Hour = now->tm_hour;
+    Date.Minute = now->tm_min;
+    Date.Second = now->tm_sec;
+
+    if (Date.Year<1900)
+    {
+
+      /* Well, this thing is copyrighted in U.S. This technique is known as
+         Windowing and you can read something about it in LinuxWeekly News:
+         http://lwn.net/1999/features/Windowing.phtml. This thing is beeing
+         written in Czech republic and Poland where algorithms are not allowed
+         to be patented. */
+
+      if (Date.Year>90)
+        Date.Year = Date.Year+1900;
+      else
+        Date.Year = Date.Year+2000;
+    }
+
+    /* FIXME: Error checking should be here. */
+    GSM->SetDateTime(&Date);
+  }
+
+  return error;
+}
+
+GSM_Error Unimplemented(void)
+{
+       return GE_NOTIMPLEMENTED;
+}
+
+GSM_Error NotSupported(void)
+{
+       return GE_NOTSUPPORTED;
+}
+
+/* Applications should call N6110_Terminate to shut down the N6110 thread and
+   close the serial port. */
+void NULL_Terminate(void)
+{
+  Protocol->Terminate();
+}
+
+#ifdef WIN32
+/* Here are things made for keeping connecting */
+void NULL_KeepAlive()
+{
+}
+#else
+/* Here are things made for keeping connecting */
+void NULL_KeepAlive()
+{
+}
+#endif
+
+#ifdef DEBUG
+void NULL_TX_DisplayMessage(u16 MessageLength, u8 *MessageBuffer)
+{
+  fprintf(stdout, _("PC: "));
+
+  txhexdump(MessageLength,MessageBuffer);
+}
+#endif
+
+bool NULL_WritePhone (u16 length, u8 *buffer) {
+  if (device_write(buffer,length)!=length) return false;
+                                      else return true;
+}
+
+GSM_Error NULL_WaitUntil (int time, GSM_Error *value)
+{
+  int timeout;
+
+  timeout=time;
+  
+  /* Wait for timeout or other error. */
+  while (timeout != 0 && *value == GE_BUSY ) {
+          
+    if (--timeout == 0)
+      return (GE_TIMEOUT);
+                    
+    usleep (100000);
+  }
+
+  return *value;
+}
+
+GSM_Error NULL_SendMessageSequence (int time, GSM_Error *value,
+                 u16 message_length, u8 message_type, u8 *buffer)
+{
+  *value=GE_BUSY;
+  
+  Protocol->SendMessage(message_length, message_type, buffer);  
+
+  return NULL_WaitUntil (time, value);
+}
+
+GSM_ConnectionType GetConnectionTypeFromString(char *Connection) {
+
+  GSM_ConnectionType connection=GCT_FBUS;
+
+  if (!strcmp(Connection, "irda"))     connection=GCT_Irda;
+  if (!strcmp(Connection, "infrared")) connection=GCT_Infrared;
+  if (!strcmp(Connection, "mbus"))     connection=GCT_MBUS;
+  if (!strcmp(Connection, "dlr3"))     connection=GCT_DLR3;
+  if (!strcmp(Connection, "at"))       connection=GCT_AT;
+  if (!strcmp(Connection, "tekram210"))connection=GCT_Tekram;
+  
+  return connection;
+}
+
+bool GetMemoryTypeString(char *memorytext, GSM_MemoryType *type)
+{
+  int i=0;
+
+  typedef struct {
+    GSM_MemoryType type;
+    char *name;
+  } GSM_MTStrings;
+
+  GSM_MTStrings mystring[] = {
+    {GMT_ME,"ME"},
+    {GMT_SM,"SM"},
+    {GMT_FD,"FD"},
+    {GMT_ON,"ON"},
+    {GMT_EN,"EN"},
+    {GMT_DC,"DC"},
+    {GMT_RC,"RC"},  
+    {GMT_MC,"MC"},
+    {GMT_LD,"LD"},
+    {GMT_MT,"MT"},
+    {GMT_ME,"undefined"}
+  };
+
+  while (strcmp(mystring[i].name,"undefined")) {
+    if (*type==mystring[i].type) {
+      strcpy(memorytext,mystring[i].name);
+      return true;
+    }
+    i++;
+  }
+  return false;
+}
+
+bool GetMemoryTypeID(char *memorytext, GSM_MemoryType *type)
+{
+  int i=0;
+
+  typedef struct {
+    GSM_MemoryType type;
+    char *name;
+  } GSM_MTStrings;
+
+  GSM_MTStrings mystring[] = {
+    {GMT_ME,"ME"},
+    {GMT_SM,"SM"},
+    {GMT_FD,"FD"},
+    {GMT_ON,"ON"},
+    {GMT_EN,"EN"},
+    {GMT_DC,"DC"},
+    {GMT_RC,"RC"},  
+    {GMT_MC,"MC"},
+    {GMT_LD,"LD"},
+    {GMT_MT,"MT"},
+    {GMT_ME,"undefined"}
+  };
+
+  while (strcmp(mystring[i].name,"undefined")) {
+    if (strcmp(mystring[i].name,memorytext)==0) {
+      *type=mystring[i].type;
+      return true;
+    }
+    i++;
+  }
+
+  return false;
+}
+
+char *GetMygnokiiVersion() {
+
+  static char Buffer[1000]="";
+
+  sprintf(Buffer, "%s",VERSION);
+
+  return Buffer;
+}
+
+/*
+1.Name,2.CodeName,3.Calendar,4.Netmonitor,5.Caller groups,6.Phonebook,
+7.Authentication 8.Datacalls 9.KeysPressing 10.SMSC Default Recipient
+11.SpeedDials 12.ScreenSavers 13.DTMF 14.SMS 15.NoPowerFrame 16.StartUpLogo
+17.Profiles 18.Ringtones 19.WAP
+*/
+
+static OnePhoneModel allmodels[] = {
+
+/*1,    2,       3,      4,       5,        6       7      8        9      10        11      12       13     14      15        16       17       18        19 */
+{"3210","NSE-8",{      0,F_NETMON,        0,      0,     0,       0,     0,F_SMSCDEF,F_SPEED,       0,     0,F_SMS  ,F_NOPOWER,F_STANIM,F_PROF51,F_RINGBIN,    0}},
+{"3210","NSE-9",{      0,F_NETMON,        0,      0,     0,       0,     0,F_SMSCDEF,F_SPEED,       0,     0,F_SMS  ,F_NOPOWER,F_STANIM,F_PROF51,F_RINGBIN,    0}},
+{"3310","NHM-5",{F_CAL33,F_NETMON,        0,F_PBK33,     0,       0,     0,F_SMSCDEF,F_SPEED,F_SCRSAV,     0,F_SMS  ,F_NOPOWER,F_STANIM,F_PROF33,F_RING_SM,    0}},
+{"3330","NHM-6",{F_CAL33,F_NETMON,        0,F_PBK33,     0,       0,F_KEYB,F_SMSCDEF,F_SPEED,F_SCRSAV,     0,F_SMS  ,        0,F_STANIM,F_PROF33,F_RING_SM,F_WAP}},
+{"5110","NSE-1",{      0,F_NETMON,        0,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,       0,F_PROF51,        0,    0}},
+{"5130","NSK-1",{      0,F_NETMON,        0,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,       0,F_PROF51,        0,    0}},
+{"5190","NSB-1",{      0,F_NETMON,        0,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,       0,F_PROF51,        0,    0}},
+{"6110","NSE-3",{F_CAL61,F_NETMON,F_CALER61,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},
+{"6130","NSK-3",{F_CAL61,F_NETMON,F_CALER61,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},
+{"6150","NSM-1",{F_CAL61,F_NETMON,F_CALER61,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},
+{"6190","NSB-3",{F_CAL61,F_NETMON,F_CALER61,      0,F_AUTH,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},
+{"6210","NPE-3",{F_CAL71,F_NETMON,F_CALER61,F_PBK71,     0,F_DATA71,     0,F_SMSCDEF,F_SPEED,       0,     0,F_SMS71,        0,F_STA62 ,F_PROF61,F_RINGBIN,F_WAP}},
+{"6250","NHM-3",{F_CAL71,F_NETMON,F_CALER61,F_PBK71,     0,F_DATA71,     0,F_SMSCDEF,F_SPEED,       0,     0,F_SMS71,        0,F_STA62 ,F_PROF61,F_RINGBIN,F_WAP}},    
+{"7110","NSE-5",{F_CAL71,F_NETMON,F_CALER61,F_PBK71,     0,F_DATA71,     0,F_SMSCDEF,F_SPEED,       0,     0,F_SMS71,        0,F_STA71 ,F_PROF61,F_RINGBIN,F_WAP}},
+{"8210","NSM-3",{      0,F_NETMON,F_CALER61,      0,     0,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},
+{"8850","NSM-2",{      0,F_NETMON,F_CALER61,      0,     0,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},
+{"9210","RAE-3",{      0,F_NETMON,F_CALER61,      0,     0,F_DATA61,F_KEYB,F_SMSCDEF,F_SPEED,       0,F_DTMF,F_SMS  ,        0,F_STA   ,F_PROF61,F_RINGBIN,    0}},//quesses only !
+{""    ,""     ,{      0,       0,        0,      0,     0,       0,     0,        0,      0,       0,     0,      0,        0,       0,       0,        0,    0}}
+};
+
+OnePhoneModel *GetPhoneModelData (const char *num)
+{
+       register int i = 0;
+
+       while (allmodels[i].number != "") {
+               if (strcmp (num, allmodels[i].number) == 0) {
+                       return (&allmodels[i]);
+               }
+               i++;
+       }
+
+       return (&allmodels[i]);
+}
+
+char *GetModelName ()
+{
+  static char model[64];
+
+  while (GSM->GetModel(model)  != GE_NONE) sleep(1);
+
+  return (GetPhoneModelData(model)->model);
+}
+
+int GetModelFeature (featnum_index num)
+{
+  static char model[64];
+
+  while (GSM->GetModel(model)  != GE_NONE) sleep(1);
+
+  return (GetPhoneModelData(model)->features[num]);
+}
+
+int LogAvailable=-1; //-1 not checked earlier, 0 not, 1 yes
+char *logfilename;
+
+bool AppendLog(u8 *buffer, int length,bool format)
+{
+  FILE *file=NULL;
+  char buffer2[50001];
+  int i;
+
+  struct CFG_Header *cfg_info;
+  char *LogFile;       
+
+  if (LogAvailable==-1) {
+
+    LogAvailable=0;
+
+    cfg_info=CFG_FindGnokiirc();
+    if (cfg_info==NULL) return false;
+
+    LogFile = CFG_Get(cfg_info, "global", "logfile");
+    if (LogFile) {
+      LogAvailable=1;
+      logfilename=LogFile;
+
+      file=fopen(logfilename, "a+");
+
+      /* We have first entry in this session and too large file */
+      if (fread( buffer2, 1, 50000,file )==50000) {
+        fclose(file);
+        file=fopen(logfilename, "w");
+      }
+    }
+  } else {
+    if (LogAvailable==1) {
+      file=fopen(logfilename, "a");
+    }
+  }
+  
+  if (LogAvailable==1) {
+    for (i=0;i<length;i++) {
+      if (format) {
+        fprintf(file, "%02x",buffer[i]);
+        switch (buffer[i]) {
+          case 0x09:fprintf(file,_(" |"));break;
+          default:
+            if (isprint(buffer[i])) fprintf(file, _("%c|"),buffer[i]);
+                               else fprintf(file, _(" |"));
+            break;
+        } 
+      } else {
+        fprintf(file, "%c",buffer[i]);
+      }
+    }
+    if (format) fprintf(file, "\n");
+    fclose(file);
+  }
+
+  return (LogAvailable==1);
+}
+
+bool AppendLogText(u8 *buffer,bool format)
+{
+  return AppendLog(buffer,strlen(buffer),format);
+}
diff --git a/common/gsm-bitmaps.c b/common/gsm-bitmaps.c
new file mode 100644 (file)
index 0000000..1e96f8b
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+       
+  Functions for manipulating bitmaps
+  
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/stat.h>
+
+#include "gsm-common.h"
+#include "gsm-bitmaps.h"
+#include "gsm-sms.h"
+#include "gsm-coding.h"
+#include "gsm-networks.h"
+
+void GSM_SetPointBitmap(GSM_Bitmap *bmp, int x, int y)
+{
+  int pixel;
+  if (bmp->type == GSM_StartupLogo || bmp->type == GSM_6210StartupLogo || bmp->type == GSM_7110StartupLogo)
+    bmp->bitmap[((y/8)*bmp->width)+x] |= 1 << (y%8);
+  if (bmp->type == GSM_OperatorLogo || bmp->type == GSM_7110OperatorLogo || bmp->type == GSM_CallerLogo) {
+    pixel=bmp->width*y + x;
+    bmp->bitmap[pixel/8] |= 1 << (7-(pixel%8));
+  }
+  if (bmp->type == GSM_PictureImage) bmp->bitmap[9*y + (x/8)] |= 1 << (7-(x%8));
+}
+  
+void GSM_ClearPointBitmap(GSM_Bitmap *bmp, int x, int y)
+{
+  int pixel;
+  if (bmp->type == GSM_StartupLogo || bmp->type == GSM_6210StartupLogo || bmp->type == GSM_7110StartupLogo)
+    bmp->bitmap[((y/8)*bmp->width)+x] &= 255 - (1 << (y%8));
+  if (bmp->type == GSM_OperatorLogo || bmp->type == GSM_7110OperatorLogo || bmp->type == GSM_CallerLogo) {
+    pixel=bmp->width*y + x;
+    bmp->bitmap[pixel/8] &= ~(1 << (7-(pixel%8)));
+  }
+  if (bmp->type == GSM_PictureImage) bmp->bitmap[9*y + (x/8)] &= 255 - (1 << (7-(x%8)));
+}
+
+bool GSM_IsPointBitmap(GSM_Bitmap *bmp, int x, int y)
+{
+  int i=0;
+  int pixel;
+
+  if (bmp->type == GSM_StartupLogo || bmp->type == GSM_6210StartupLogo || bmp->type == GSM_7110StartupLogo)
+    i=(bmp->bitmap[((y/8)*bmp->width) + x] & 1<<((y%8)));
+  if (bmp->type == GSM_OperatorLogo || bmp->type == GSM_7110OperatorLogo || bmp->type == GSM_CallerLogo) {
+    pixel=bmp->width*y + x;
+    i=(bmp->bitmap[pixel/8] & 1<<(7-(pixel%8)));
+  }
+  if (bmp->type == GSM_PictureImage) i=(bmp->bitmap[9*y + (x/8)] & 1<<(7-(x%8)));
+
+  if (i) return true; else return false;
+}
+  
+void GSM_ClearBitmap(GSM_Bitmap *bmp)
+{
+  int i;
+  for (i=0;i<bmp->size;i++) bmp->bitmap[i]=0;
+}
+
+GSM_Error GSM_ReadBitmap(GSM_SMSMessage *message, GSM_Bitmap *bitmap)
+{
+  int offset = 1;
+  unsigned char buffer[20];
+
+  switch (message->UDHType) {
+  case GSM_OpLogo:
+    EncodeUDHHeader(buffer, GSM_OperatorLogo);
+    if (message->Length!=133) return GE_UNKNOWN;
+    
+    bitmap->type = GSM_OperatorLogo;
+
+    DecodeNetworkCode(message->MessageText, bitmap->netcode);
+
+    offset = 4;
+    break;
+
+  case GSM_CallerIDLogo:
+    EncodeUDHHeader(buffer, GSM_CallerLogo);
+    if (message->Length!=130) return GE_UNKNOWN;
+    
+    bitmap->type=GSM_CallerLogo;
+
+    break;
+  default: /* error */
+    return GE_UNKNOWN;
+    break;
+  }
+  bitmap->width = message->MessageText[offset];
+  bitmap->height = message->MessageText[offset + 1];
+  
+  if (bitmap->width!=72 || bitmap->height!=14) return GE_INVALIDIMAGESIZE;
+  
+  bitmap->size = (bitmap->width * bitmap->height) / 8;
+  memcpy(bitmap->bitmap, message->MessageText + offset + 3, bitmap->size);
+
+#ifdef DEBUG
+  fprintf(stdout, _("Bitmap from SMS: width %i, height %i\n"),bitmap->width,bitmap->height);
+#endif
+
+  return GE_NONE;
+}
+
+void GSM_ResizeBitmap(GSM_Bitmap *bitmap, GSM_Bitmap_Types target)
+{
+  GSM_Bitmap backup;
+  int x,y,width,height;
+  
+  backup=*bitmap;
+      
+  if (target==GSM_StartupLogo) {
+    bitmap->width=84;
+    bitmap->height=48;
+  }
+  if (target==GSM_7110StartupLogo) {
+    bitmap->width=96;
+    bitmap->height=65;
+  }
+  if (target==GSM_6210StartupLogo) {
+    bitmap->width=96;
+    bitmap->height=60;
+  }
+  if (target==GSM_OperatorLogo || target==GSM_CallerLogo) {
+    bitmap->width=72;
+    bitmap->height=14;
+  }
+  if (target==GSM_PictureImage ) {
+    bitmap->width=72;
+    bitmap->height=28;
+  }
+  if (target==GSM_7110OperatorLogo) {
+    bitmap->width=78;
+    bitmap->height=21;
+  }
+  bitmap->type=target;
+  bitmap->size=(bitmap->width*bitmap->height + 7)/8;
+  
+  width=backup.width;
+  if (bitmap->width<width) {
+    width=bitmap->width;
+#ifdef DEBUG
+    fprintf(stdout,_("We lost some part of image - it's cut (width from %i to %i) !\n"),backup.width,width);
+#endif /* DEBUG */
+  }
+  
+  height=backup.height;
+  if (bitmap->height<height) {
+    height=bitmap->height; 
+#ifdef DEBUG
+    fprintf(stdout,_("We lost some part of image - it's cut (height from %i to %i) !\n"),backup.height,height);
+#endif /* DEBUG */
+  }
+  
+  GSM_ClearBitmap(bitmap);
+  
+  for (y=0;y<height;y++) {
+    for (x=0;x<width;x++)
+      if (GSM_IsPointBitmap(&backup,x,y)) GSM_SetPointBitmap(bitmap,x,y);
+  }
+  
+//GSM_PrintBitmap(&backup);
+//GSM_PrintBitmap(bitmap);
+}
+
+void GSM_PrintBitmap(GSM_Bitmap *bitmap)
+{
+  int x,y;
+
+  for (y=0;y<bitmap->height;y++) {
+    for (x=0;x<bitmap->width;x++) {
+      if (GSM_IsPointBitmap(bitmap,x,y)) {
+        fprintf(stdout, _("#"));
+      } else {
+        fprintf(stdout, _(" "));
+      }
+    }
+    fprintf(stdout, _("\n"));
+  }
+}
+
+int GSM_SaveBitmapToSMS(GSM_MultiSMSMessage *SMS, GSM_Bitmap *bitmap,
+                        bool ScreenSaver, bool UnicodeText)
+{
+  char MessageBuffer[GSM_MAX_SMS_8_BIT_LENGTH*4];
+  int MessageLength=0;
+  GSM_UDH UDHType=GSM_NoUDH;
+  
+  switch (bitmap->type) {
+    case GSM_OperatorLogo:
+      UDHType=GSM_OpLogo;
+
+      EncodeNetworkCode(MessageBuffer, bitmap->netcode);
+      MessageLength=3;
+
+      /* Set the logo size */
+      MessageBuffer[MessageLength++] = 0x00;
+      MessageBuffer[MessageLength++] = bitmap->width;
+      MessageBuffer[MessageLength++] = bitmap->height;
+      MessageBuffer[MessageLength++] = 0x01;
+
+      memcpy(MessageBuffer+MessageLength,bitmap->bitmap,bitmap->size);
+      MessageLength=MessageLength+bitmap->size;
+
+      break;
+    case GSM_CallerLogo:
+      UDHType=GSM_CallerIDLogo;
+
+      /* Set the logo size */
+      MessageBuffer[MessageLength++] = 0x00;
+      MessageBuffer[MessageLength++] = bitmap->width;
+      MessageBuffer[MessageLength++] = bitmap->height;
+      MessageBuffer[MessageLength++] = 0x01;
+
+      memcpy(MessageBuffer+MessageLength,bitmap->bitmap,bitmap->size);
+      MessageLength=MessageLength+bitmap->size;
+
+      break;
+    case GSM_PictureImage:
+      UDHType=GSM_ProfileUDH;
+
+      MessageBuffer[MessageLength++]=0x30;     //SM version. Here 3.0
+
+      if (!ScreenSaver)
+        MessageBuffer[MessageLength++]=SM30_OTA; //ID for OTA bitmap
+      else
+        MessageBuffer[MessageLength++]=SM30_SCREENSAVER; //ID for screen saver
+
+      /* Length for picture part */
+      MessageBuffer[MessageLength++]=0x01;     //length hi
+      MessageBuffer[MessageLength++]=0x00;     //length lo
+
+      /* Set the logo size */
+      MessageBuffer[MessageLength++] = 0x00;
+      MessageBuffer[MessageLength++] = bitmap->width;
+      MessageBuffer[MessageLength++] = bitmap->height;
+      MessageBuffer[MessageLength++] = 0x01;
+
+      memcpy(MessageBuffer+MessageLength,bitmap->bitmap,bitmap->size);
+      MessageLength=MessageLength+bitmap->size;
+
+      if (strlen(bitmap->text)!=0) {
+        if (UnicodeText) {
+          MessageBuffer[MessageLength++]=SM30_UNICODETEXT; //ID for Unicode text
+
+          /* Length for text part */
+          MessageBuffer[MessageLength++]=0x00;             //length of text1
+          MessageBuffer[MessageLength++]=strlen(bitmap->text)*2;//length of text2
+
+          EncodeUnicode (MessageBuffer+MessageLength,bitmap->text,strlen(bitmap->text));
+          MessageLength=MessageLength+2*strlen(bitmap->text);
+        } else {
+          MessageBuffer[MessageLength++]=SM30_ISOTEXT;    //ID for ISO-8859-1 text
+
+          /* Length for text part */
+          MessageBuffer[MessageLength++]=0x00;            //length of text1
+          MessageBuffer[MessageLength++]=strlen(bitmap->text); //length of text2
+
+          memcpy(MessageBuffer+MessageLength,bitmap->text,strlen(bitmap->text));
+          MessageLength=MessageLength+strlen(bitmap->text);
+       }
+      }
+      break;
+    
+    default: /* error */
+      break;
+  }
+        
+  GSM_MakeMultiPartSMS2(SMS,MessageBuffer,MessageLength, UDHType, GSM_Coding_Default);
+
+  return 0;
+}
diff --git a/common/gsm-networks.c b/common/gsm-networks.c
new file mode 100644 (file)
index 0000000..b36bcf5
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+       
+  This file implements GSM networks searching.
+
+*/
+
+#include <string.h>
+
+#include "gsm-common.h"
+#include "gsm-networks.h"
+#include "gsm-coding.h"
+
+GSM_Country GSM_Countries[] =
+{
+  { "202", "Greece" },
+  { "204", "Netherlands" },
+  { "206", "Belgium" },
+  { "208", "France" },
+  { "213", "Andorra" },
+  { "214", "Spain" },
+  { "216", "Hungary" },
+  { "218", "Bosnia Herzegovina" },
+  { "219", "Croatia" },
+  { "220", "Yugoslavia" },
+  { "222", "Italy" },
+  { "226", "Romania" },
+  { "228", "Switzerland" },
+  { "230", "Czech Republic" },
+  { "231", "Slovak Republic" },
+  { "232", "Austria" },
+  { "234", "United Kingdom" },
+  { "238", "Denmark" },
+  { "240", "Sweden" },
+  { "242", "Norway" },
+  { "244", "Finland" },
+  { "246", "Lithuania" },
+  { "247", "Latvia" },
+  { "248", "Estonia" },
+  { "250", "Russia" },
+  { "255", "Ukraine" },
+  { "259", "Moldova" },
+  { "260", "Poland" },
+  { "262", "Germany" },
+  { "266", "Gibraltar" },
+  { "268", "Portugal" },
+  { "270", "Luxembourg" },
+  { "272", "Ireland" },
+  { "274", "Iceland" },
+  { "276", "Albania" },
+  { "278", "Malta" },
+  { "280", "Cyprus" },
+  { "282", "Georgia" },
+  { "283", "Armenia" },
+  { "284", "Bulgaria" },
+  { "286", "Turkey" },
+  { "290", "Greenland" },
+  { "293", "Slovenia" },
+  { "294", "Macedonia" },
+  { "302", "Canada" },
+  { "310", "Papua New Guinea" },
+  { "310", "U.S.A." },
+  { "340", "French West Indies" },
+  { "400", "Azerbaijan" },
+  { "404", "India" },
+  { "410", "Pakistan" },
+  { "413", "Sri Lanka" },
+  { "415", "Lebanon" },
+  { "416", "Jordan" },
+  { "417", "Syria" },
+  { "418", "Iraq" },
+  { "419", "Kuwait" },
+  { "420", "Saudi Arabia" },
+  { "422", "Oman" },
+  { "424", "United Arab Emirates" },
+  { "425", "Israel" },
+  { "426", "Bahrain" },
+  { "427", "Qatar" },
+  { "432", "Iran" },
+  { "434", "Uzbekistan" },
+  { "437", "Kyrgyz Republic" },
+  { "452", "Vietnam" },
+  { "454", "Hong Kong" },
+  { "455", "Macau" },
+  { "456", "Cambodia" },
+  { "457", "Lao" },
+  { "460", "China" },
+  { "466", "Taiwan" },
+  { "470", "Bangladesh" },
+  { "502", "Malaysia" },
+  { "505", "Australia" },
+  { "510", "Indonesia" },
+  { "515", "Philippines" },
+  { "520", "Thailand" },
+  { "525", "Singapore" },
+  { "528", "Brunei Darussalam" },
+  { "530", "New Zealand" },
+  { "542", "Fiji" },
+  { "546", "New Caledonia" },
+  { "547", "French Polynesia" },
+  { "602", "Egypt" },
+  { "603", "Algeria" },
+  { "604", "Morocco" },
+  { "605", "Tunisia" },
+  { "608", "Senegal" },
+  { "611", "Guinea" },
+  { "612", "Cote d'Ivoire" },
+  { "615", "Togo" },
+  { "617", "Mauritius" },
+  { "618", "Liberia" },
+  { "620", "Ghana" },
+  { "624", "Cameroon" },
+  { "625", "Cape Verde" },
+  { "633", "Seychelles" },
+  { "634", "Mozambique" },
+  { "634", "Sudan" },
+  { "635", "Rwanda" },
+  { "636", "Ethiopia" },
+  { "640", "Tanzania" },
+  { "641", "Uganda" },
+  { "645", "Zambia" },
+  { "646", "Madagascar" },
+  { "647", "Reunion" },
+  { "648", "Zimbabwe" },
+  { "649", "Namibia" },
+  { "650", "Malawi" },
+  { "651", "Lesotho" },
+  { "652", "Botswana" },
+  { "655", "South Africa" },
+  { "730", "Chile" },
+  { "734", "Venezuela" },
+
+  { "undefined", "unknown" }
+};
+
+GSM_Network GSM_Networks[] =
+{
+  { "202 01", "Cosmote" },
+  { "202 05", "PANAFON" },
+  { "202 10", "TELESTET" },
+  { "204 04", "LIBERTEL" },
+  { "204 08", "KPN Telecom" },
+  { "204 12", "Telfort" },
+  { "204 16", "BEN" },
+  { "204 20", "Dutchtone NV" },
+  { "206 01", "PROXIMUS" },
+  { "206 10", "Mobistar" },
+  { "208 01", "ITINERIS" },
+  { "208 10", "SFR" },
+  { "208 20", "Bouygues Telecom" },
+  { "213 03", "MOBILAND" },
+  { "214 01", "Airtel GSM 900-Spain" },
+  { "214 03", "Retevision Movil" },
+  { "214 07", "MOVISTAR" },
+  { "216 01", "Pannon GSM" },
+  { "216 70", "Vodafone" },
+  { "216 30", "Westel 900" },
+  { "218 90", "GSMBIH" },
+  { "219 01", "CRONET" },
+  { "219 10", "VIP" },
+  { "220 01", "MOBTEL" },
+  { "220 02", "ProMonte GSM" },
+  { "222 01", "Telecom Italia Mobile" },
+  { "222 10", "OMNITEL" },
+  { "222 88", "Wind Telecomunicazioni SpA" },
+  { "226 01", "CONNEX GSM" },
+  { "226 10", "DIALOG" },
+  { "228 01", "NATEL International" },
+  { "228 02", "diAx Mobile AG" },
+  { "230 01", "Paegas" },
+  { "230 02", "EuroTel" },
+  { "230 03", "Oskar" },
+  { "231 01", "Globtel" },
+  { "231 02", "EuroTel GSM" },
+  { "232 01", "A1" },
+  { "232 03", "max.mobil." },
+  { "232 05", "ONE" },
+  { "232 07", "tele.ring" },
+  { "234 10", "Cellnet" },
+  { "234 15", "Vodafone" },
+  { "234 30", "One 2 One" },
+  { "234 33", "ORANGE" },
+  { "234 50", "Jersey Telecoms GSM" },
+  { "234 55", "Guernsey Telecoms GSM" },
+  { "234 58", "PRONTO GSM" },
+  { "238 01", "TDK-MOBIL" },
+  { "238 02", "SONOFON" },
+  { "238 20", "TELIA DK" },
+  { "238 30", "Mobilix" },
+  { "240 01", "Telia AB" },
+  { "240 07", "COMVIQ" },
+  { "240 08", "EUROPOLITAN" },
+  { "242 01", "Telenor Mobil" },
+  { "242 02", "NetCom GSM" },
+  { "244 03", "Telia City (Finland)" },
+  { "244 05", "Radiolinja" },
+  { "244 09", "Finnet" },
+  { "244 91", "Sonera" },
+  { "246 01", "OMNITEL" },
+  { "246 02", "Bite GSM" },
+  { "247 01", "LMT LV" },
+  { "247 02", "BALTCOM GSM" },
+  { "248 01", "EMT GSM" },
+  { "248 02", "Radiolinja Eesti AS" },
+  { "248 03", "Q GSM" },
+  { "250 01", "Mobile Telesystems" },
+  { "250 02", "North-West GSM" },
+  { "250 05", "Siberian Cellular Systems 900" },
+  { "250 07", "BM Telecom" },
+  { "250 10", "Don Telecom" },
+  { "250 12", "FECS-900" },
+  { "250 13", "Kuban GSM" },
+  { "250 39", "Uraltel" },
+  { "250 44", "North Caucasian GSM" },
+  { "250 99", "BeeLine" },
+  { "255 01", "UMC" },
+  { "255 02", "WellCOM" },
+  { "255 03", "Kyivstar" },
+  { "255 05", "Golden Telecom" },
+  { "259 01", "VOXTEL" },
+  { "260 01", "PLUS GSM" },
+  { "260 02", "ERA GSM" },
+  { "260 03", "IDEA Centertel" },
+  { "262 01", "D1 TELEKOM" },
+  { "262 02", "D2 PRIVAT" },
+  { "262 03", "E-Plus" },
+  { "262 07", "Interkom" },
+  { "266 01", "Gibtel GSM" },
+  { "268 01", "TELECEL" },
+  { "268 03", "OPTIMUS" },
+  { "268 06", "TMN" },
+  { "270 01", "LUXGSM" },
+  { "270 77", "TANGO" },
+  { "272 01", "EIRCELL-GSM" },
+  { "272 02", "Digifone" },
+  { "274 01", "Landssiminn GSM 900" },
+  { "274 02", "TAL hf" },
+  { "276 01", "AMC" },
+  { "278 01", "Vodafone Malta Limited" },
+  { "280 01", "CYTAGSM" },
+  { "282 01", "Geocell Limited" },
+  { "282 02", "Magti GSM" },
+  { "283 01", "ArmGSM" },
+  { "284 01", "M-TEL GSM BG" },
+  { "286 01", "Turkcell" },
+  { "286 02", "TELSIM GSM" },
+  { "288 01", "Faroese Telecom" },
+  { "290 01", "Tele Greenland" },
+  { "293 40", "SI.MOBIL d. d." },
+  { "293 41", "MOBITEL" },
+  { "294 01", "MobiMak" },
+  { "302 37", "Microcell Connexions Inc" },
+  { "310 01", "Cellnet" },
+  { "310 02", "Sprint Spectrum" },
+  { "310 11", "Wireless 2000 Telephone Co." },
+  { "310 15", "BellSouth Mobility DCS" },
+  { "310 16", "Omnipoint" },
+  { "310 17", "Pac Bell" },
+  { "310 26", "VOICESTREAM" },
+  { "310 27", "Powertel" },
+  { "310 31", "AERIAL" },
+  { "310 77", "Iowa Wireless Services LP" },
+  { "340 01", "AMERIS" },
+  { "400 01", "AZERCELL GSM" },
+  { "400 02", "Bakcell GSM 2000" },
+
+  { "404 01", "AIRCELL"},\r
+  { "404 02", "Evergrowth"},\r
+  { "404 03", "Bharthi Telenet"},\r
+  { "404 05", "Fascel"},\r
+  { "404 07", "Tata Cell"},\r //TATA Cellular
+  { "404 08", "Koshika"},
+  { "404 09", "Reliance"},\r
+  { "404 10", "AIRTEL"},\r //AirTel
+  { "404 11", "Essar"},\r //Essar Cellphone
+  { "404 12", "Escotel"},\r
+  { "404 14", "Spice"},\r //Modicom
+  { "404 15", "AIRCELL"},\r //Essar Cellphone
+  { "404 18", "Reliance"},\r
+  { "404 19", "Escotel"},\r
+  { "404 20", "Orange"},\r //Max Touch
+  { "404 21", "BPL Mobile"},\r //BPL - Mobile
+  { "404 22", "Birla AT&T"},\r
+  { "404 24", "Birla AT&T"},\r
+  { "404 26", "Koshika"},\r
+  { "404 27", "BPL Mobile"},\r //BPL USWEST Cellular
+  { "404 30", "Usha"},\r //Command
+  { "404 31", "AIRCELL"},\r //?
+  { "404 31", "Command"},\r //?
+  { "404 32", "Koshika"},\r
+  { "404 34", "Koshika"},\r
+  { "404 36", "Reliance"},\r
+  { "404 40", "SkyCell"},\r
+  { "404 41", "RPG"}, //RPG Cellular
+  { "404 42", "AIRCELL"},\r //AIRCEL
+  { "404 43", "BPL Mobile"},\r
+  { "404 44", "Spice"},\r
+  { "404 45", "AIRTEL-blr"},\r
+  { "404 46", "BPL Moibile"},\r
+  { "404 49", "AIRTEL"},
+  { "404 50", "Reliance"},\r
+  { "404 52", "Reliance"},
+  { "404 56", "Escotel"},
+  { "404 60", "AIRCELL"},\r
+  { "404 67", "Reliance"},
+  { "404 70", "Hexacom"},\r
+  { "404 78", "RPG"},\r
+  { "404 85", "Reliance"},\r
+\r
+  { "410 01", "Mobilink" },
+  { "413 02", "DIALOG GSM" },
+  { "415 01", "CELLIS" },
+  { "415 03", "LIBANCELL" },
+  { "416 01", "Fastlink" },
+  { "417 09", "MOBILE SYRIA" },
+  { "419 02", "MTCNet" },
+  { "420 01", "Al Jawwal" },
+  { "420 07", "E.A.E" },
+  { "422 02", "GTO" },
+  { "424 02", "UAE-ETISALAT" },
+  { "425 01", "Partner Communications Company Ltd" },
+  { "426 01", "BHR MOBILE PLUS" },
+  { "427 01", "QATARNET" },
+  { "432 11", "TCI" },
+  { "434 04", "Daewoo Unitel" },
+  { "434 05", "Coscom" },
+  { "437 01", "Bitel" },
+  { "454 00", "TCSL GSM" },
+  { "454 04", "HKGHT" },
+  { "454 06", "SMARTONE GSM" },
+  { "454 10", "New World PCS" },
+  { "454 12", "PEOPLES" },
+  { "454 16", "SUNDAY" },
+  { "455 01", "TELEMOVEL+ GSM900-Macau" },
+  { "456 01", "MobiTel" },
+  { "456 02", "SAMART-GSM" },
+  { "457 01", "Lao Shinawatra Telecom" },
+  { "460 00", "China Telecom GSM" },
+  { "460 01", "CU-GSM" },
+  { "466 01", "Far EasTone Telecoms 900" },
+  { "466 06", "TUNTEX GSM 1800" },
+  { "466 88", "KG Telecom" },
+  { "466 92", "Chunghwa GSM" },
+  { "466 93", "MobiTai" },
+  { "466 97", "TWNGSM" },
+  { "466 99", "TransAsia" },
+  { "470 01", "GrameenPhone Ltd" },
+  { "470 19", "Mobile 2000" },
+  { "502 12", "Maxis Mobile" },
+  { "502 13", "TM Touch" },
+  { "502 16", "DiGi 1800" },
+  { "502 17", "ADAM" },
+  { "502 19", "CELCOM" },
+  { "505 01", "MobileNet" },
+  { "505 02", "OPTUS" },
+  { "505 03", "VODAFONE" },
+  { "505 08", "One.Tel" },
+  { "510 01", "SATELINDO" },
+  { "510 10", "TELKOMSEL" },
+  { "510 11", "Excelcom" },
+  { "515 01", "ISLACOM" },
+  { "515 02", "Globe Telecom" },
+  { "520 01", "AIS GSM" },
+  { "520 10", "WCS" },
+  { "520 18", "Worldphone 1800" },
+  { "520 23", "HELLO" },
+  { "525 01", "SingTel Mobile" },
+  { "525 02", "ST-PCN" },
+  { "525 03", "MOBILEONE" },
+  { "528 11", "DSTCom" },
+  { "530 01", "Vodafone New Zealand Limited" },
+  { "542 01", "Vodafone" },
+  { "546 01", "Mobilis" },
+  { "547 20", "VINI" },
+  { "602 01", "MobiNil" },
+  { "602 02", "Tunicell" },
+  { "603 01", "ALGERIAN MOBILE NETWORK" },
+  { "604 01", "I A M" },
+  { "608 01", "ALIZE" },
+  { "611 02", "Lagui" },
+  { "612 03", "IVOIRIS" },
+  { "612 05", "Telecel" },
+  { "615 01", "TOGO CELL" },
+  { "617 01", "Cellplus Mobile Comms" },
+  { "618 01", "Omega" },
+  { "620 01", "SPACEFON" },
+  { "625 01", "CVMOVEL" },
+  { "633 01", "Seychelles Cellular Services" },
+  { "633 10", "AIRTEL" },
+  { "634 01", "MobiTel" },
+  { "635 10", "Rwandacell" },
+  { "636 01", "ETMTN" },
+  { "640 01", "TRITEL" },
+  { "641 10", "MTN-Uganda" },
+  { "642 02", "ANTARIS" },
+  { "643 01", "T.D.M GSM 900" },
+  { "645 01", "ZAMCELL" },
+  { "646 01", "Madacom" },
+  { "646 03", "Sacel Madagascar S.A." },
+  { "647 10", "SRR" },
+  { "648 01", "NET*ONE" },
+  { "648 03", "Telecel" },
+  { "649 01", "MTC" },
+  { "650 01", "Callpoint 900" },
+  { "651 01", "Vodacom Lesotho (Pty) Ltd" },
+  { "655 01", "Vodacom" },
+  { "655 10", "MTN" },
+  { "680 38", "NPI Wireless" },
+  { "730 01", "Entel Telefonia Movi" },
+  { "730 10", "Entel PCS" },
+  { "734 01", "Infonet" },
+
+  { "undefined", "unknown" }
+};
+
+char *GSM_GetNetworkName(char *NetworkCode)
+{
+
+  int index=0;
+
+  while ( strcmp(GSM_Networks[index].Code, NetworkCode) &&
+          strcmp(GSM_Networks[index].Code, "undefined") )
+    index++;
+
+  return GSM_Networks[index].Name;
+}
+
+char *GSM_GetNetworkCode(char *NetworkName)
+{
+
+  int index=0;
+
+  while ( strcmp(GSM_Networks[index].Name, NetworkName) &&
+          strcmp(GSM_Networks[index].Code, "undefined") )
+    index++;
+
+  return GSM_Networks[index].Code;
+}
+
+char *GSM_GetCountryName(char *CountryCode)
+{
+
+  int index=0;
+
+  while ( strncmp(GSM_Countries[index].Code, CountryCode, 3) &&
+          strcmp(GSM_Countries[index].Code, "undefined") )
+    index++;
+
+  return GSM_Countries[index].Name;
+}
+
+char *GSM_GetCountryCode(char *CountryName)
+{
+
+  int index=0;
+
+  while ( strcmp(GSM_Countries[index].Name, CountryName) &&
+          strcmp(GSM_Countries[index].Code, "undefined") )
+    index++;
+
+  return GSM_Countries[index].Code;
+}
+
+void EncodeNetworkCode(unsigned char* buffer, unsigned char* output)
+{
+    EncodeBCD (buffer, output, 6, false);
+    buffer[1]=buffer[1] | 0xf0;
+}
+
+void DecodeNetworkCode(unsigned char* buffer, unsigned char* output)
+{
+    DecodeBCD (output, buffer, 3);
+    output[6]=output[5];
+    output[5]=output[4];
+    output[4]=output[3];
+    output[3]=' ';
+}
diff --git a/common/gsm-ringtones.c b/common/gsm-ringtones.c
new file mode 100644 (file)
index 0000000..aa68378
--- /dev/null
@@ -0,0 +1,1023 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+  This file provides support for ringtones.
+
+*/
+
+#ifdef WIN32
+  #include <windows.h>
+  #include "misc_win32.h"
+#else
+  #include <unistd.h>
+#endif
+
+#include "gsm-api.h"
+
+GSM_Ringtone SMringtone;
+GSM_BinRingtone ringtone;
+
+/* Beats-per-Minute Encoding */
+
+int BeatsPerMinute[] = {
+   25,  28,  31,  35,  40,  45,  50,  56,  63,  70,
+   80,  90, 100, 112, 125, 140, 160, 180, 200, 225,
+  250, 285, 320, 355, 400, 450, 500, 565, 635, 715,
+  800, 900
+};
+
+struct OneRingtone RingingTones[] = {
+         {"",0,0},
+/*  1 */ {"Uploaded #1",0,0},   /*  2 */ {"Ring ring",0,0},
+/*  3 */ {"Low",0,0},           /*  4 */ {"Fly",0,0},
+/*  5 */ {"Mosquito",0,0},      /*  6 */ {"Bee",0,0},
+/*  7 */ {"Intro",0,0},         /*  8 */ {"Etude",0,0},
+/*  9 */ {"Hunt",0,0},          /* 10 */ {"Going up",0,0},
+/* 11 */ {"City bird",0,0},     /* 12 */ {"Chase",0,0},
+/* 13 */ {"Scifi",0,0},         /* 14 */ {"Kick",0,0},
+/* 15 */ {"Do-mi-so",0,0},      /* 16 */ {"Robo N1X",0,0},
+/* 17 */ {"Dizzy",0,0},         /* 18 */ {"Playground",0,0},
+/* 19 */ {"That's it!",0,0},    /* 20 */ {"Grande valse",0,0},
+/* 21 */ {"Knock knock",0,0},   /* 22 */ {"Knock again",0,0},
+/* 23 */ {"Helan",0,0},         /* 24 */ {"Fuga",0,0},          
+/* 25 */ {"Menuet",0,0},        /* 26 */ {"Ode to Joy",0,0},
+/* 27 */ {"Elise",0,0},         /* 28 */ {"Mozart 40",0,0},
+/* 29 */ {"Piano Concerto",0,0},/* 30 */ {"William Tell",0,0},
+/* 31 */ {"Badinerie",0,0},     /* 32 */ {"Polka",0,0},
+/* 33 */ {"Attraction",0,0},    /* 34 */ {"Polite",0,0},
+/* 35 */ {"Persuasion",0,0},    /* 36 */ {"Tick tick",0,0},
+/* 37 */ {"Samba",0,0},         /* 38 */ {"Orient",0,0},
+/* 39 */ {"Charleston",0,0},    /* 40 */ {"Songette",0,0},    
+/* 41 */ {"Jumping",0,0},       /* 42 */ {"Lamb",0,0},
+/* 43 */ {"Marry",0,0},         /* 44 */ {"Tango",0,0},
+/* 45 */ {"Tangoed",0,0},       /* 46 */ {"Down",0,0},
+/* 47 */ {"Polska",0,0},        /* 48 */ {"WalzeBrilliant",0,0},
+/* 49 */ {"Cicada",0,0},        /* 50 */ {"Trio",0,0},
+/* 51 */ {"Circles",0,0},       /* 52 */ {"Nokia tune",0,0},
+/* 53 */ {"Sunny walks",0,0},   /* 54 */ {"Basic rock",0,0},
+/* 55 */ {"Reveille",0,0},      /* 56 */ {"Groovy Blue",0,0},
+/* 57 */ {"Brave Scotland",0,0},/* 58 */ {"Matilda",0,0},
+/* 59 */ {"Bumblebee",0,0},     /* 60 */ {"Hungarian",0,0},
+/* 61 */ {"Valkyrie",0,0},      /* 62 */ {"Bach #3",0,0},
+/* 63 */ {"Toreador",0,0},      /* 64 */ {"9th Symphony",0,0},
+/* 65 */ {"Uploaded #2",0,0},   /* 66 */ {"Uploaded #3",0,0},
+/* 67 */ {"Uploaded #4",0,0},   /* 68 */ {"Uploaded #5",0,0},
+         {"",0,0}
+};
+
+int OctetAlign(unsigned char *Dest, int CurrentBit)
+{
+  int i=0;
+
+  while((CurrentBit+i)%8) {
+    ClearBit(Dest, CurrentBit+i);
+    i++;
+  }
+
+  return CurrentBit+i;
+}
+
+int OctetAlignNumber(int CurrentBit)
+{
+  int i=0;
+
+  while((CurrentBit+i)%8) { i++; }
+
+  return CurrentBit+i;
+}
+
+int BitPack(unsigned char *Dest, int CurrentBit, unsigned char *Source, int Bits)
+{
+
+  int i;
+
+  for (i=0; i<Bits; i++)
+    if (GetBit(Source, i))   SetBit(Dest, CurrentBit+i);
+                      else ClearBit(Dest, CurrentBit+i);
+
+  return CurrentBit+Bits;
+}
+
+int GSM_GetTempo(int Beats) {
+
+  int i=0;
+
+  while ( i < sizeof(BeatsPerMinute)/sizeof(BeatsPerMinute[0])) {
+
+    if (Beats<=BeatsPerMinute[i]) break;
+    i++;
+  }
+
+  return i<<3;
+}    
+
+int BitPackByte(unsigned char *Dest, int CurrentBit, unsigned char Command, int Bits) {
+
+  unsigned char Byte[]={Command};
+
+  return BitPack(Dest, CurrentBit, Byte, Bits);
+}
+
+
+/* This is messy but saves using the math library! */
+
+int GSM_GetDuration(int number, unsigned char *spec) {
+
+  int duration=0;
+
+  switch (number) {
+
+  case 128*3/2: duration=Duration_Full; *spec=DottedNote;        break;  
+  case 128*2/3: duration=Duration_Full; *spec=Length_2_3;        break;  
+  case 128    : duration=Duration_Full; *spec=NoSpecialDuration; break;  
+  case 64*9/4 : duration=Duration_1_2;  *spec=DoubleDottedNote;  break;    
+  case 64*3/2 : duration=Duration_1_2;  *spec=DottedNote;        break;  
+  case 64*2/3 : duration=Duration_1_2;  *spec=Length_2_3;        break;  
+  case 64     : duration=Duration_1_2;  *spec=NoSpecialDuration; break;  
+  case 32*9/4 : duration=Duration_1_4;  *spec=DoubleDottedNote;  break;    
+  case 32*3/2 : duration=Duration_1_4;  *spec=DottedNote;        break;  
+  case 32*2/3 : duration=Duration_1_4;  *spec=Length_2_3;        break;  
+  case 32     : duration=Duration_1_4;  *spec=NoSpecialDuration; break;  
+  case 16*9/4 : duration=Duration_1_8;  *spec=DoubleDottedNote;  break;    
+  case 16*3/2 : duration=Duration_1_8;  *spec=DottedNote;        break;  
+  case 16*2/3 : duration=Duration_1_8;  *spec=Length_2_3;        break;  
+  case 16     : duration=Duration_1_8;  *spec=NoSpecialDuration; break;  
+  case 8*9/4  : duration=Duration_1_16; *spec=DoubleDottedNote;  break;    
+  case 8*3/2  : duration=Duration_1_16; *spec=DottedNote;        break;  
+  case 8*2/3  : duration=Duration_1_16; *spec=Length_2_3;        break;  
+  case 8      : duration=Duration_1_16; *spec=NoSpecialDuration; break;  
+  case 4*9/4  : duration=Duration_1_32; *spec=DoubleDottedNote;  break;    
+  case 4*3/2  : duration=Duration_1_32; *spec=DottedNote;        break;  
+  case 4*2/3  : duration=Duration_1_32; *spec=Length_2_3;        break;  
+  case 4      : duration=Duration_1_32; *spec=NoSpecialDuration; break;  
+  }
+
+  return duration;
+}
+
+
+int GSM_GetNote(int number) {
+  
+  int note=0;
+  if (number!=255) {
+    note=number%14;
+    switch (note) {
+
+    case  0: note=Note_C;   break;
+    case  1: note=Note_Cis; break;
+    case  2: note=Note_D;   break;
+    case  3: note=Note_Dis; break;
+    case  4: note=Note_E;   break;
+    case  6: note=Note_F;   break;
+    case  7: note=Note_Fis; break;
+    case  8: note=Note_G;   break;
+    case  9: note=Note_Gis; break;
+    case 10: note=Note_A;   break;
+    case 11: note=Note_Ais; break;
+    case 12: note=Note_H;   break;
+    }
+  }
+  else note = Note_Pause;
+
+  return note;
+
+}
+
+int GSM_GetScale(int number) {
+
+  int scale=-1;
+
+  if (number!=255) {
+    scale=number/14;
+
+    /* Ensure the scale is valid */
+    scale%=4;
+
+    scale=scale<<6;
+  }
+  return scale;
+}
+
+/* This function packs the ringtone from the structure "ringtone" to
+   "package", where maxlength means length of package.
+   Function returns number of packed notes and change maxlength to
+   number of used chars in "package" */
+u8 GSM_PackRingtone(GSM_Ringtone *ringtone, unsigned char *package, int *maxlength)
+{
+  int StartBit=0;
+  unsigned char CommandLength = 0x02;
+  unsigned char spec;
+  int oldscale=10, newscale=0, oldstyle=0, oldtempo=0;
+  int HowMany=0;              /* How many instructions packed */
+  int HowLong=0;              /* How many bits packed */
+  int StartNote=0, EndNote=0; /* First and last packed note from ringtone */
+
+  /* Default ringtone parameters */
+  u8 DefNoteScale=2, DefNoteDuration=4;
+  int DefNoteTempo=63;
+  u8 DefNoteStyle=NaturalStyle;
+  
+  int buffer[6];\r              /* Used to find default ringtone parameters */
+  int i,j,k=0,thisnote,thisnotelong;\r
+  
+  /* Find the most frequently used duration and use this for the default */\r
\r for (i=0;i<6;i++) buffer[i]=0;\r
+  for (i=0;i<ringtone->NrNotes;i++) {\r
+    switch (ringtone->notes[i].duration) {\r
+      case 192: buffer[0]++; break;\r
+      case 128: buffer[0]++; break;\r
+      case  96:        buffer[1]++; break;\r
+      case  64: buffer[1]++; break;\r
+      case  48: buffer[2]++; break;\r
+      case  32: buffer[2]++; break;\r
+      case  24: buffer[3]++; break;\r
+      case  16: buffer[3]++; break;\r
+      case  12: buffer[4]++; break;\r
+      case   8: buffer[4]++; break;\r
+      case   6: buffer[5]++; break;\r
+      case   4: buffer[5]++; break;\r
+    }\r
+  }\r
+\r
+  /* Now find the most frequently used */\r
+  j=0;\r
+  for (i=0;i<6;i++) {\r
+    if (buffer[i]>j) {\r
+      k=i; \r
+      j=buffer[i];\r
+    }\r
+  }\r
+\r
+  /* Finally convert the default duration */\r
+  switch (k) {\r
+      case 0: DefNoteDuration=128; break;      \r
+      case 1: DefNoteDuration= 64; break;      \r
+      case 2: DefNoteDuration= 32; break;      \r
+      case 3: DefNoteDuration= 16; break;      \r
+      case 4: DefNoteDuration=  8; break;      \r
+      case 5: DefNoteDuration=  4; break;      \r
+     default: DefNoteDuration= 16; break;      \r
+  }  \r
+\r
+  /* Find the most frequently used scale and use this for the default */\r\r
+  for (i=0;i<6;i++) buffer[i]=0;\r
+  for (i=0;i<ringtone->NrNotes;i++) {\r
+    if (ringtone->notes[i].note!=255) {\r
+      buffer[ringtone->notes[i].note/14]++;\r
+    }\r
+  }\r
+  j=0;\r
+  for (i=0;i<6;i++) {\r
+    if (buffer[i]>j) {\r
+      DefNoteScale=i;\r
+      j=buffer[i];\r
+    }\r
+  }\r
+
+  StartBit=BitPackByte(package, StartBit, CommandLength, 8);
+  StartBit=BitPackByte(package, StartBit, RingingToneProgramming, 7);
+
+  /* The page 3-23 of the specs says that <command-part> is always
+     octet-aligned. */
+  StartBit=OctetAlign(package, StartBit);
+
+  StartBit=BitPackByte(package, StartBit, Sound, 7);
+  StartBit=BitPackByte(package, StartBit, BasicSongType, 3);
+
+  /* Set special chars in ringtone name */
+  for (i=0;i<strlen(ringtone->name);i++) {
+    if (ringtone->name[i]=='~') ringtone->name[i]=1; //enables/disables blinking
+    if (ringtone->name[i]=='`') ringtone->name[i]=0; //hides rest ot contents
+  }
+
+  /* Packing the name of the tune. */
+  StartBit=BitPackByte(package, StartBit, strlen(ringtone->name)<<4, 4);
+  StartBit=BitPack(package, StartBit, ringtone->name, 8*strlen(ringtone->name));
+
+  /* Set special chars in ringtone name */
+  for (i=0;i<strlen(ringtone->name);i++) {
+    if (ringtone->name[i]==1) ringtone->name[i]='~'; //enables/disables blinking
+    if (ringtone->name[i]==0) ringtone->name[i]='`'; //hides rest ot contents
+  }
+
+  /* Info about song pattern */
+  StartBit=BitPackByte(package, StartBit, 0x01, 8); /* One song pattern */
+  StartBit=BitPackByte(package, StartBit, PatternHeaderId, 3);
+  StartBit=BitPackByte(package, StartBit, A_part, 2);
+  StartBit=BitPackByte(package, StartBit, ringtone->Loop<<4, 4);
+
+  /* Info, how long is contents for SMS */
+  HowLong=8+8+7+3+4+8*strlen(ringtone->name)+8+3+2+4+8+3+2+3+5;
+  
+  /* Calculating number of instructions in the tune.
+     Each Note contains Note and (sometimes) Scale.
+     Default Tempo and Style are instructions too. */
+  HowMany=2; /* Default Tempo and Style */
+
+  /* Default style and tempo */
+  DefNoteStyle=ringtone->notes[0].style;
+  DefNoteTempo=ringtone->notes[0].tempo;
+  oldstyle=DefNoteStyle;
+  oldtempo=DefNoteTempo;
+
+  for(i=0; i<ringtone->NrNotes; i++) {
+
+    /* PC Composer 2.0.010 doesn't like, when we start ringtone from pause:
+       displays, that format is invalid and
+       hangs, when you move mouse over place, where pause is */       
+    if (GSM_GetNote(ringtone->notes[i].note)==Note_Pause && oldscale==10) {
+      StartNote++;
+    } else {
+
+      thisnote=0;
+      thisnotelong=0;
+     
+      /* we don't write Scale/Style info before "Pause" note - it saves place */
+      if (GSM_GetNote(ringtone->notes[i].note)!=Note_Pause) {
+
+        if (ringtone->allnotesscale ||
+            oldscale!=(newscale=GSM_GetScale(ringtone->notes[i].note))) {
+
+          /* We calculate, if we have space to add next scale instruction */
+          if (((OctetAlignNumber(HowLong+5)+8)/8)<=(*maxlength)) {
+            oldscale=newscale;
+            HowLong+=5;
+            HowMany++;
+            thisnote++;
+            thisnotelong+=5;
+         } else {
+           break;
+         }
+       }
+       if (ringtone->notes[i].style!=oldstyle) {
+          /* We calculate, if we have space to add next style instruction */
+          if (((OctetAlignNumber(HowLong+5)+8)/8)<=(*maxlength)) {
+           oldstyle=ringtone->notes[i].style;
+            HowLong+=5;
+            HowMany++;
+            thisnote++;
+            thisnotelong+=5;
+         } else {
+            HowLong=HowLong-thisnotelong;
+            HowMany=HowMany-thisnote;
+            break;
+         }
+       }
+      }
+      
+      if (ringtone->notes[i].tempo!=oldtempo) {
+        /* We calculate, if we have space to add next tempo instruction */
+        if (((OctetAlignNumber(HowLong+8)+8)/8)<=(*maxlength)) {
+          oldtempo=ringtone->notes[i].tempo;
+          HowLong+=8;
+          HowMany++;
+          thisnote++;
+          thisnotelong+=8;
+       } else {
+          HowLong=HowLong-thisnotelong;
+          HowMany=HowMany-thisnote;
+          break;
+        }
+      }
+    
+      /* We calculate, if we have space to add next note instruction */
+      if (((OctetAlignNumber(HowLong+12)+8)/8)<=(*maxlength)) {
+        HowMany++;
+        EndNote++;
+        HowLong+=12;
+      } else {
+        HowLong=HowLong-thisnotelong;
+        HowMany=HowMany-thisnote;
+        break;
+      }
+    }
+
+    /* We are sure, we pack it for SMS or setting to phone, not for OTT file */    
+    if (*maxlength<1000) {
+       /* Like Pc Composer say - before of phone limitations...*/
+      if ((EndNote-StartNote)==FB61_MAX_RINGTONE_NOTES-1) break;
+    }
+  }
+
+  StartBit=BitPackByte(package, StartBit, HowMany, 8);
+#ifdef DEBUG
+//  fprintf(stdout,_("length of new pattern: %i %i\n"),HowMany,StartBit);
+#endif
+
+  /* Style */
+  StartBit=BitPackByte(package, StartBit, StyleInstructionId, 3);
+  StartBit=BitPackByte(package, StartBit, DefNoteStyle, 2);
+    
+  /* Beats per minute/tempo of the tune */
+  StartBit=BitPackByte(package, StartBit, TempoInstructionId, 3);
+  StartBit=BitPackByte(package, StartBit, GSM_GetTempo(DefNoteTempo), 5);
+#ifdef DEBUG
+//  fprintf(stdout,_("def temp: %i %i\n"),GSM_GetTempo(DefNoteTempo),StartBit);
+#endif
+
+  /* Default scale */
+  oldscale=10;
+  
+  /* Default style */
+  oldstyle=DefNoteStyle;
+
+  /* Default tempo */
+  oldtempo=DefNoteTempo;
+
+  /* Notes packing */
+  for(i=StartNote; i<(EndNote+StartNote); i++) {
+
+    /* we don't write Scale info before "Pause" note - it saves place */
+    if (GSM_GetNote(ringtone->notes[i].note)!=Note_Pause) {
+      if (ringtone->allnotesscale ||
+          oldscale!=(newscale=GSM_GetScale(ringtone->notes[i].note))) {
+#ifdef DEBUG
+//    fprintf(stdout,_("Scale\n"));
+#endif
+        oldscale=newscale;
+        StartBit=BitPackByte(package, StartBit, ScaleInstructionId, 3);
+        StartBit=BitPackByte(package, StartBit, GSM_GetScale(ringtone->notes[i].note), 2);
+      }
+      if (ringtone->notes[i].style!=oldstyle) {
+        /* Style */
+        StartBit=BitPackByte(package, StartBit, StyleInstructionId, 3);
+        StartBit=BitPackByte(package, StartBit, ringtone->notes[i].style, 2);
+        oldstyle=ringtone->notes[i].style;
+      }
+    }
+
+    if (ringtone->notes[i].tempo!=oldtempo) {
+      /* Beats per minute/tempo of the tune */
+      StartBit=BitPackByte(package, StartBit, TempoInstructionId, 3);
+      StartBit=BitPackByte(package, StartBit, GSM_GetTempo(ringtone->notes[i].tempo), 5);
+      oldtempo=ringtone->notes[i].tempo;
+    }    
+    
+    /* Note */
+    StartBit=BitPackByte(package, StartBit, NoteInstructionId, 3);
+    StartBit=BitPackByte(package, StartBit, GSM_GetNote(ringtone->notes[i].note), 4);
+    StartBit=BitPackByte(package, StartBit, GSM_GetDuration(ringtone->notes[i].duration,&spec), 3);
+    StartBit=BitPackByte(package, StartBit, spec, 2);
+
+#ifdef DEBUG    
+//    fprintf(stdout,_("note(%i): %i, scale: %i, duration: %i, spec: %i\n"),i,ringtone->notes[i].note,GSM_GetScale(ringtone->notes[i].note),GSM_GetDuration(ringtone->notes[i].duration,&spec),spec);
+#endif
+
+  }
+
+  StartBit=OctetAlign(package, StartBit);
+
+  StartBit=BitPackByte(package, StartBit, CommandEnd, 8);
+  
+#ifdef DEBUG
+  if (StartBit!=OctetAlignNumber(HowLong)+8)
+    fprintf(stdout,_("Error in PackRingtone - StartBit different to HowLong %d - %d)\n"),StartBit,OctetAlignNumber(HowLong)+8);
+#endif
+
+  *maxlength=StartBit/8;  
+
+  return(EndNote+StartNote);
+}
+
+int BitUnPack(unsigned char *Dest, int CurrentBit, unsigned char *Source, int Bits)
+{
+  int i;
+
+  for (i=0; i<Bits; i++)
+    if (GetBit(Dest, CurrentBit+i)) {   SetBit(Source, i); }
+                               else { ClearBit(Source, i); }
+
+  return CurrentBit+Bits;
+}
+
+int BitUnPackInt(unsigned char *Src, int CurrentBit, int *integer, int Bits) {
+
+  int l=0,z=128,i;
+
+  for (i=0; i<Bits; i++) {
+    if (GetBit(Src, CurrentBit+i)) l=l+z;
+    z=z/2;
+  }
+
+  *integer=l;
+  
+  return CurrentBit+i;
+}
+
+int OctetUnAlign(int CurrentBit)
+{
+  int i=0;
+
+  while((CurrentBit+i)%8) i++;
+
+  return CurrentBit+i;
+}
+
+/* TODO: better checking, if contents of ringtone is OK */
+GSM_Error GSM_UnPackRingtone(GSM_Ringtone *ringtone, char *package, int maxlength)
+{
+  int StartBit=0;
+  int HowMany;
+  int l,q,i;
+  int spec;
+
+  /* Default ringtone parameters */
+  u8 DefNoteScale=2, DefNoteDuration=4;
+  int DefNoteTempo=63;
+  u8 DefNoteStyle=NaturalStyle;
+
+  ringtone->allnotesscale=false;
+  
+  StartBit=BitUnPackInt(package,StartBit,&l,8);
+#ifdef DEBUG
+  if (l!=0x02)
+    fprintf(stdout,_("Not header\n"));  
+#endif
+  if (l!=0x02) return GE_SUBFORMATNOTSUPPORTED;
+
+  StartBit=BitUnPackInt(package,StartBit,&l,7);    
+#ifdef DEBUG
+  if (l!=RingingToneProgramming)
+    fprintf(stdout,_("Not RingingToneProgramming\n"));  
+#endif
+  if (l!=RingingToneProgramming) return GE_SUBFORMATNOTSUPPORTED;
+    
+  /* The page 3-23 of the specs says that <command-part> is always
+     octet-aligned. */
+  StartBit=OctetUnAlign(StartBit);
+
+  StartBit=BitUnPackInt(package,StartBit,&l,7);    
+#ifdef DEBUG
+  if (l!=Sound)
+    fprintf(stdout,_("Not Sound\n"));  
+#endif
+  if (l!=Sound) return GE_SUBFORMATNOTSUPPORTED;
+
+  StartBit=BitUnPackInt(package,StartBit,&l,3);    
+#ifdef DEBUG
+  if (l!=BasicSongType)
+    fprintf(stdout,_("Not BasicSongType\n"));  
+#endif
+  if (l!=BasicSongType) return GE_SUBFORMATNOTSUPPORTED;
+
+  /* Getting length of the tune name */
+  StartBit=BitUnPackInt(package,StartBit,&l,4);
+  l=l>>4;
+#ifdef DEBUG
+//  fprintf(stdout,_("Length of name: %i\n"),l);
+#endif
+
+  /* Unpacking the name of the tune. */
+  StartBit=BitUnPack(package, StartBit, ringtone->name, 8*l);
+  ringtone->name[l]=0;
+
+  /* Set special chars in ringtone name */
+  for (i=0;i<strlen(ringtone->name);i++) {
+    if (ringtone->name[i]==1) ringtone->name[i]='~'; //enables/disables blinking
+    if (ringtone->name[i]==0) ringtone->name[i]='`'; //hides rest ot contents
+  }
+
+#ifdef DEBUG
+//   fprintf(stdout,_("Name: %s\n"),ringtone->name);
+#endif
+
+  StartBit=BitUnPackInt(package,StartBit,&l,8);    
+#ifdef DEBUG
+//  fprintf(stdout,_("Number of song patterns: %i\n"),l);
+#endif
+  if (l!=1) return GE_SUBFORMATNOTSUPPORTED; //we support only one song pattern
+
+  StartBit=BitUnPackInt(package,StartBit,&l,3);          
+#ifdef DEBUG
+  if (l!=PatternHeaderId)
+    fprintf(stdout,_("Not PatternHeaderId\n"));
+#endif
+  if (l!=PatternHeaderId) return GE_SUBFORMATNOTSUPPORTED;
+
+  StartBit+=2; //Pattern ID - we ignore it
+
+  StartBit=BitUnPackInt(package,StartBit,&l,4);          
+  l=l>>4;
+#ifdef DEBUG
+  fprintf(stdout,_("Loop value: %i\n"),l);
+#endif
+  ringtone->Loop=l;
+
+  HowMany=0;
+  StartBit=BitUnPackInt(package, StartBit, &HowMany, 8);
+#ifdef DEBUG
+  fprintf(stdout,_("length of new pattern: %i %i\n"),HowMany,StartBit);
+#endif
+
+  ringtone->NrNotes=0;
+    
+  for (i=0;i<HowMany;i++) {
+
+    StartBit=BitUnPackInt(package,StartBit,&q,3);              
+    switch (q) {
+      case VolumeInstructionId:
+#ifdef DEBUG
+//        fprintf(stdout,_("Volume\n"));
+#endif
+        StartBit+=4;
+        break;
+      case StyleInstructionId:
+        StartBit=BitUnPackInt(package,StartBit,&l,2);              
+#ifdef DEBUG
+//     fprintf(stdout,_("Style %i\n"),l>>6);
+#endif
+       switch (l) {
+         case StaccatoStyle  : DefNoteStyle=StaccatoStyle;   break;
+         case ContinuousStyle: DefNoteStyle=ContinuousStyle; break;
+         case NaturalStyle   : DefNoteStyle=NaturalStyle;    break;
+       }
+       break;
+      case TempoInstructionId:
+        StartBit=BitUnPackInt(package,StartBit,&l,5);                  
+        l=l>>3;
+        DefNoteTempo=BeatsPerMinute[l];
+#ifdef DEBUG
+//     fprintf(stdout,_("Tempo %i\n"),l);
+#endif
+        break;
+      case ScaleInstructionId:
+        StartBit=BitUnPackInt(package,StartBit,&l,2);
+       DefNoteScale=l>>6;
+#ifdef DEBUG
+//     fprintf(stdout,_("scale: %i %i\n"),DefNoteScale,ringtone->NrNotes);
+#endif
+       break;
+      case NoteInstructionId:
+        StartBit=BitUnPackInt(package,StartBit,&l,4);    
+
+        switch (l) {
+          case Note_C  :ringtone->notes[ringtone->NrNotes].note=0;break;
+          case Note_Cis:ringtone->notes[ringtone->NrNotes].note=1;break;
+          case Note_D  :ringtone->notes[ringtone->NrNotes].note=2;break;
+          case Note_Dis:ringtone->notes[ringtone->NrNotes].note=3;break;
+          case Note_E  :ringtone->notes[ringtone->NrNotes].note=4;break;
+          case Note_F  :ringtone->notes[ringtone->NrNotes].note=6;break;
+          case Note_Fis:ringtone->notes[ringtone->NrNotes].note=7;break;
+          case Note_G  :ringtone->notes[ringtone->NrNotes].note=8;break;
+          case Note_Gis:ringtone->notes[ringtone->NrNotes].note=9;break;
+          case Note_A  :ringtone->notes[ringtone->NrNotes].note=10;break;
+          case Note_Ais:ringtone->notes[ringtone->NrNotes].note=11;break;
+          case Note_H  :ringtone->notes[ringtone->NrNotes].note=12;break;
+          default      :ringtone->notes[ringtone->NrNotes].note=255;break; //Pause ?
+        }
+      
+        if (ringtone->notes[ringtone->NrNotes].note!=255)
+          ringtone->notes[ringtone->NrNotes].note=ringtone->notes[ringtone->NrNotes].note+DefNoteScale*14;
+
+        StartBit=BitUnPackInt(package,StartBit,&l,3);    
+       DefNoteDuration=l;
+
+        StartBit=BitUnPackInt(package,StartBit,&spec,2);    
+
+        if (DefNoteDuration==Duration_Full && spec==DottedNote)
+            ringtone->notes[ringtone->NrNotes].duration=128*3/2;
+        if (DefNoteDuration==Duration_Full && spec==Length_2_3)
+            ringtone->notes[ringtone->NrNotes].duration=128*2/3;
+        if (DefNoteDuration==Duration_Full && spec==NoSpecialDuration)
+            ringtone->notes[ringtone->NrNotes].duration=128;
+        if (DefNoteDuration==Duration_1_2 && spec==DottedNote)
+            ringtone->notes[ringtone->NrNotes].duration=64*3/2;
+        if (DefNoteDuration==Duration_1_2 && spec==Length_2_3)
+            ringtone->notes[ringtone->NrNotes].duration=64*2/3;
+        if (DefNoteDuration==Duration_1_2 && spec==NoSpecialDuration)
+            ringtone->notes[ringtone->NrNotes].duration=64;
+        if (DefNoteDuration==Duration_1_4 && spec==DottedNote)
+            ringtone->notes[ringtone->NrNotes].duration=32*3/2;
+        if (DefNoteDuration==Duration_1_4 && spec==Length_2_3)
+            ringtone->notes[ringtone->NrNotes].duration=32*2/3;
+        if (DefNoteDuration==Duration_1_4 && spec==NoSpecialDuration)
+            ringtone->notes[ringtone->NrNotes].duration=32;
+        if (DefNoteDuration==Duration_1_8 && spec==DottedNote)
+            ringtone->notes[ringtone->NrNotes].duration=16*3/2;
+        if (DefNoteDuration==Duration_1_8 && spec==Length_2_3)
+            ringtone->notes[ringtone->NrNotes].duration=16*2/3;
+        if (DefNoteDuration==Duration_1_8 && spec==NoSpecialDuration)
+            ringtone->notes[ringtone->NrNotes].duration=16;
+        if (DefNoteDuration==Duration_1_16 && spec==DottedNote)
+            ringtone->notes[ringtone->NrNotes].duration=8*3/2;
+        if (DefNoteDuration==Duration_1_16 && spec==Length_2_3)
+            ringtone->notes[ringtone->NrNotes].duration=8*2/3;
+        if (DefNoteDuration==Duration_1_16 && spec==NoSpecialDuration)
+            ringtone->notes[ringtone->NrNotes].duration=8;
+        if (DefNoteDuration==Duration_1_32 && spec==DottedNote)
+            ringtone->notes[ringtone->NrNotes].duration=4*3/2;
+        if (DefNoteDuration==Duration_1_32 && spec==Length_2_3)
+            ringtone->notes[ringtone->NrNotes].duration=4*2/3;
+        if (DefNoteDuration==Duration_1_32 && spec==NoSpecialDuration)
+            ringtone->notes[ringtone->NrNotes].duration=4;
+
+        ringtone->notes[ringtone->NrNotes].style=DefNoteStyle;
+
+        ringtone->notes[ringtone->NrNotes].tempo=DefNoteTempo;
+       
+#ifdef DEBUG    
+//    fprintf(stdout,_("note(%i): %i, scale: %i, duration: %i, spec: %i\n"),ringtone->NrNotes,ringtone->notes[ringtone->NrNotes].note,DefNoteScale,DefNoteDuration,spec);
+#endif
+        if (ringtone->NrNotes==FB61_MAX_RINGTONE_NOTES) break;
+       
+        ringtone->NrNotes++;
+        break;
+      default:
+#ifdef DEBUG
+    fprintf(stdout,_("Unsupported block %i %i\n"),q,i);  
+#endif
+        return GE_SUBFORMATNOTSUPPORTED;
+    } 
+  }
+
+#ifdef DEBUG
+//  printf("Number of notes=%d\n",ringtone->NrNotes);
+#endif
+
+  return GE_NONE;
+}
+
+GSM_Error GSM_ReadRingtone(GSM_SMSMessage *message, GSM_Ringtone *ringtone)
+{
+  if (message->UDHType==GSM_RingtoneUDH) {
+    return GSM_UnPackRingtone(ringtone, message->MessageText, message->Length);
+  } else return GE_SUBFORMATNOTSUPPORTED;
+}
+
+int GSM_GetFrequency(int number) {
+  
+  int freq=0;
+
+  /* Values according to the software from http://iki.fi/too/sw/xring/
+     generated with:
+     perl -e 'print int(4400 * (2 **($_/12)) + .5)/10, "\n" for(3..14)'
+  */ 
+  if (number!=255) {
+    freq=number%14;
+    switch (freq) {
+
+    case  0: freq=523.3; break; // C
+    case  1: freq=554.4; break; // Cis
+
+    case  2: freq=587.3; break; //D
+    case  3: freq=622.3; break; //Dis
+    
+    case  4: freq=659.3; break; //E
+
+    case  6: freq=698.5; break; //F
+    case  7: freq=740;   break; //Fis
+
+    case  8: freq=784;   break; //G
+    case  9: freq=830.6; break; //Gis
+
+    case 10: freq=880;   break; //A
+    case 11: freq=932.3; break; //Ais
+    
+    case 12: freq=987.8; break; //H
+
+    default: freq=0; break;
+    }
+  }
+  else freq = 0;
+
+  if ((number/14)!=0) freq=freq*(number/14);
+                else freq=freq/2;
+
+  return freq;
+
+}
+
+/* Very fast hack. It should be written correctly ! */
+void GSM_PlayOneNote (GSM_RingtoneNote note) {
+  int Hz;
+
+  Hz=GSM_GetFrequency(note.note);
+       
+  GSM->PlayTone(Hz,5);
+
+  /* Is it correct ? Experimental values here */
+  switch (note.style) {
+    case StaccatoStyle:
+      usleep (7500);
+      GSM->PlayTone(0,0);      
+      usleep ((1500000/note.tempo*note.duration)-(7500));
+      break;
+    case ContinuousStyle:
+      usleep  (1500000/note.tempo*note.duration);
+      break;
+    case NaturalStyle:
+      usleep  (1500000/note.tempo*note.duration-50);
+      GSM->PlayTone(0,0);      
+      usleep (50);
+      break;   
+  }
+}
+
+void GSM_PlayRingtone (GSM_Ringtone *ringtone) {
+
+  int i;
+  
+  for (i=0;i<ringtone->NrNotes;i++) {
+     GSM_PlayOneNote(ringtone->notes[i]);
+  }      
+
+  /* Disables buzzer */
+  GSM->PlayTone(255*255,0);  
+}
+
+/* Initializes one ringtone: first is number of ringtone in 
+   RingingTones in gnokii.h, second its' code, last position in phone menu */
+void RT(int number, int code, int menu) {
+  RingingTones[number].code=code;
+  RingingTones[number].menu=menu;
+}
+
+/* This function initializes structures with ringtones names adequate for
+   your phone model and firmware; if your phone is not now supported:
+   1.set first ringtone in 1'st profile in your phone
+   2.make ./gnokii --getprofile 1
+   3.read ringtone code
+   4.see in gnokii.h, if ringtone name is gnokii.h in RingingTones (if not, add)
+   5.put here RT(a,b,c), where a is number of name in RingingTones in gnokii.h,
+     b is its' code and c is number of ringtone in phone menu
+   6.repeat steps 1-5 for all ringtones
+   7.send me (Marcin-Wiacek@Topnet.PL) all RT and phone model */
+void PrepareRingingTones(char model[64], char rev[64]) {
+
+  char rev2[64];
+  int i;
+  bool doit;
+
+  if (!RingingTones[0].code) {
+    if (!strcmp(model,"NSE-1")) //5110
+    {
+      RT( 2,18, 1);RT( 3,19, 2);RT( 7,23, 3);RT( 5,21, 4);RT( 9,25, 5);
+                   RT(20,48, 7);RT(11,27, 8);RT(33,59, 9);RT(35,62,10);
+      RT(46,60,11);RT(16,36,12);RT(17,37,13);RT(13,32,14);RT(14,34,15);
+      RT(19,43,16);RT(18,39,17);RT(24,50,18);RT(31,57,19);RT(28,54,20);
+      RT(30,56,21);RT(40,73,22);RT(39,72,23);RT(37,69,24);
+      RT(23,49,26);RT(38,71,27);             RT(41,74,29);
+      
+      strcpy(rev2,"05.23"); //5.24 and higher
+      doit=false;
+      for(i=0;i<5;i++)
+      {
+        if (rev[i]<rev2[i]) break;
+       if (rev[i]>rev2[i]) doit=true;
+      }
+      
+      if (doit) {
+         RT(22,47, 6);RT(47,58,25);RT(45,80,28);RT(43,75,30);
+      } else
+      {
+         RT(21,47, 6);RT(32,58,25);RT(44,80,28);RT(42,75,30);
+      }
+      RingingTones[0].menu=30; /* How many ringtones in phone */
+    }
+    if (!strcmp(model,"NSM-1")) //6150
+    {
+      RT( 2,18, 1);RT( 3,19, 2);RT( 7,23, 3);RT( 6,22, 4);
+      RT( 4,20, 5);RT( 5,21, 6);RT(15,35, 7);RT(12,30, 8);
+      RT( 9,25, 9);RT(20,47,10);RT( 8,24,11);RT(11,27,12);
+      RT(10,26,13);RT(34,60,14);RT(33,58,15);RT(35,61,16);
+      RT(16,36,17);RT(13,32,18);RT(19,43,19);RT(18,39,20);
+      RT(24,49,21);RT(31,56,22);RT(25,50,23);RT(27,52,24);
+      RT(26,51,25);RT(28,53,26);RT(29,54,27);RT(30,55,28);
+      RT(39,71,29);RT(37,68,30);RT(47,57,31);RT(23,48,32);
+      RT(38,70,33);RT(36,67,34);RT(41,73,35);
+      /*uploadable ringtone*/
+      RT( 1,17,36);
+      RingingTones[0].menu=36; /* How many ringtones in phone */
+    }
+    if (!strcmp(model,"NPE-3")) //6210
+    {
+      RT(19,64, 1);RT( 2,65, 2);RT( 3,66, 3);RT(15,67, 4);RT( 6,68, 5);
+      RT(49,69, 6);RT(50,70, 7);RT( 7,71, 8);RT(35,72, 9);RT(33,73,10);
+      RT(18,74,11);RT( 5,75,12);RT(51,76,13);RT(52,77,14);RT(53,78,15);
+      RT(37,79,16);RT(54,80,17);RT(55,81,18);RT(56,82,19);RT(57,83,20);
+      RT(58,84,21);RT(59,85,22);RT(25,86,23);RT(27,87,24);RT(30,88,25);
+      RT(39,89,26);RT(24,90,27);RT( 8,91,28);RT(60,92,29);RT(61,93,30);
+      RT(31,94,31);RT(62,95,32);RT(63,96,33);RT(64,97,34);RT(48,98,35);
+      /* Uploadable ringtones */
+      RT( 1,137,36);RT(65,138,37);RT(66,139,38);RT(67,140,39);RT(68,141,40);
+
+      RingingTones[0].menu=40; /* How many ringtones in phone */
+    }
+    RingingTones[0].code=true;
+  }
+}
+
+/* returns names from code or number in menu */
+char *RingingToneName(int code, int menu)
+{
+  int index=1,i;
+  GSM_Error error;
+
+  if (code==0)
+  {
+    while (strcmp(RingingTones[index].name,"")) {
+      if (RingingTones[index].menu==menu) break;
+      index++;
+    }
+  } else
+  {
+    while (strcmp(RingingTones[index].name,"")) {
+      if (RingingTones[index].code==code) break;
+      index++;
+    }
+  }
+
+  if (!strncmp(RingingTones[index].name,"Uploaded ",9)) {
+     ringtone.location=atoi(&RingingTones[index].name[10]);
+
+     error=GSM->GetBinRingtone(&ringtone);
+
+     if (error==GE_NONE) return ringtone.name;
+     if (error==GE_UNKNOWNMODEL) {
+    
+        /* In 33x we have normal "Smart Messaging" format */
+        if (GetModelFeature (FN_RINGTONES)==F_RING_SM) {
+      
+          i=7;
+         if (ringtone.frame[9]==0x4a && ringtone.frame[10]==0x3a) i=8;
+         ringtone.frame[i]=0x02;
+       
+          GSM_UnPackRingtone(&SMringtone, ringtone.frame+i, ringtone.length-i);
+
+          return SMringtone.name;
+       }
+     }
+  }
+  
+  return RingingTones[index].name;
+}
+
+/* returns code from number in menu */
+int RingingToneCode(int menu)
+{
+  int index=1;
+
+  while ( RingingTones[index].menu!=menu) index++;
+
+  return RingingTones[index].code;
+}
+
+/* returns number in menu from code */
+int RingingToneMenu(int code)
+{
+  int index=1;
+
+  while ( RingingTones[index].code!=code) index++;
+
+  return RingingTones[index].menu;
+}
+
+int NumberOfRingtones()
+{
+  return RingingTones[0].menu;
+}
+
+int GSM_SaveRingtoneToSMS(GSM_MultiSMSMessage *SMS,
+                          GSM_Ringtone *ringtone, bool profilestyle)
+{  
+  int i, j;
+  unsigned char MessageBuffer[GSM_MAX_SMS_8_BIT_LENGTH*4];
+  unsigned char MessageBuffer2[GSM_MAX_SMS_8_BIT_LENGTH*4];
+  int MessageLength;
+  GSM_UDH UDHType;
+  
+  EncodeUDHHeader(MessageBuffer, GSM_RingtoneUDH);
+  MessageLength=GSM_MAX_SMS_8_BIT_LENGTH-(MessageBuffer[0]+1);
+  i=GSM_PackRingtone(ringtone, MessageBuffer, &MessageLength);
+
+  if (i!=ringtone->NrNotes && profilestyle)
+  {
+    MessageLength=0;
+    MessageBuffer[MessageLength++]=0x30;          //SM version. Here 3.0
+    MessageBuffer[MessageLength++]=SM30_RINGTONE; //ID for ringtone
+
+    MessageBuffer[MessageLength++]=0x01;          //length hi.Later changed
+    MessageBuffer[MessageLength++]=0x00;          //length lo.Later changed
+
+    j=SM30_MAX_RINGTONE_FRAME_LENGTH;
+    i=GSM_PackRingtone(ringtone, MessageBuffer2, &j);
+    MessageLength=MessageLength+j;
+    memcpy(MessageBuffer+4,MessageBuffer2,j);
+      
+    MessageBuffer[2]=j/256;
+    MessageBuffer[3]=j%256;
+
+    UDHType=GSM_ProfileUDH;
+  } else
+    UDHType=GSM_RingtoneUDH;
+
+  GSM_MakeMultiPartSMS2(SMS,MessageBuffer,MessageLength, UDHType, GSM_Coding_Default);
+
+  return i;
+}
diff --git a/common/gsm-sms.c b/common/gsm-sms.c
new file mode 100644 (file)
index 0000000..5a35034
--- /dev/null
@@ -0,0 +1,1020 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+       
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "gsm-api.h"
+#include "gsm-coding.h"
+
+/* User data headers */
+GSM_UDHHeader UDHHeaders[] = {
+  { GSM_ConcatenatedMessages, 0x05, "\x00\x03\x01\x00\x00" },
+      /* See GSM 03.40 section 9.2.3.24.1 */
+  { GSM_DisableVoice,         0x04, "\x01\x02\x00\x00" },
+  { GSM_DisableFax,           0x04, "\x01\x02\x01\x00" },
+  { GSM_DisableEmail,         0x04, "\x01\x02\x02\x00" },
+  { GSM_EnableVoice,          0x04, "\x01\x02\x00\x01" },
+  { GSM_EnableFax,            0x04, "\x01\x02\x01\x01" },
+  { GSM_EnableEmail,          0x04, "\x01\x02\x02\x01" },
+      /* See GSM 03.40 section 9.2.3.24.2 for voice, fax and email messages */
+  { GSM_VoidSMS,              0x08, "\x01\x02\x02\x01\x01\x02\x02\x00" },
+      /* When send such SMS to some phones, they don't display anything,
+         only beep and enable vibra/light */
+  { GSM_BugSMS,               0x09, "\x01\x02\x02\x01\x01\x02\x02\x00\xc0" },
+
+  /* Short Smart Messaging UDH */
+  /* General format: */
+  /* 1 byte  0x05     : IEI application port addressing scheme, 16 bit address */
+  /* 1 byte  0x04     : IEI length */
+  /* 2 bytes          : destination address: high & low byte */
+  /* 2 bytes 0x00 0x00: originator address: high & low byte */
+  { GSM_RingtoneUDH,          0x06, "\x05\x04\x15\x81\x00\x00" },
+  { GSM_OpLogo,               0x06, "\x05\x04\x15\x82\x00\x00" },
+  { GSM_CallerIDLogo,         0x06, "\x05\x04\x15\x83\x00\x00" },
+  { GSM_WAPBookmarkUDH,       0x06, "\x05\x04\xc3\x4f\x00\x00" },
+
+  /* Long Smart Messaging UDH */
+  /* General format: */
+  /* 1 byte 0x05      : IEI application port addressing scheme, 16 bit address */
+  /* 1 byte 0x04      : IEI length */
+  /* 2 bytes 0x00 0x00: destination address: high & low byte */
+  /* 2 bytes 0x00 0x00: originator address: high & low byte */
+  /* 1 byte 0x00      : null byte for end first part ? */
+  /* 1 byte 0x03      : length for rest ? */
+  /* 1 byte                               */
+  /* 1 byte           : number of all SMS */
+  /* 1 byte           : number of current SMS */
+  { GSM_CalendarNoteUDH,      0x0b, "\x05\x04\x00\xe4\x00\x00\x00\x03\xc7\x00\x00" }, //from 3310 ?
+  { GSM_CalendarNoteUDH2,     0x0b, "\x05\x04\x23\xf5\x00\x00\x00\x03\x01\x00\x00" }, //from 6210 or 9210 Note: last 0x01 changes according to note type
+  { GSM_ProfileUDH,           0x0b, "\x05\x04\x15\x8a\x00\x00\x00\x03\xce\x00\x00" },
+  { GSM_WAPBookmarkUDH,       0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x01\x00\x00" },//note last 0x01 can change
+  { GSM_WAPSettingsUDH,       0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x7f\x00\x00" },
+  { GSM_PhonebookUDH,         0x0b, "\x05\x04\x23\xf4\x00\x00\x00\x03\x01\x00\x00" },
+
+  { GSM_NoUDH,                0x00, "" }
+};
+
+#define ByteMask ((1 << Bits) - 1)
+
+int GSM_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 = EncodeWithDefaultAlphabet(*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);
+}
+
+int GSM_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;
+}
+
+/* This function implements packing of numbers (SMS Center number and
+   destination number) for SMS sending function. */
+/* See GSM 03.40 9.1.1:
+   1 byte  - length of number given in semioctets or bytes (when given in bytes,
+             includes one byte for byte with number format.
+             Returned by function (set semioctet to true, if want result
+             in semioctets).
+   1 byte  - format of number. See GSM_NumberType; in gsm-common.h. Returned
+             in u8 *Output.
+   n bytes - 2n or 2n-1 semioctets with number. For some types of numbers
+             in the most significant bits of the last byte with 0x0f
+             (1111 binary) are filled if the number is represented
+             with odd number of digits. Returned in u8 *Output. */
+/* 1 semioctet = 4 bits = half of byte */
+int GSM_PackSemiOctetNumber(u8 *Number, u8 *Output, bool semioctet) {
+
+  u8 *IN=Number;  /* Pointer to the input number */
+  u8 *OUT=Output; /* Pointer to the output */
+  int length=0,j;
+  unsigned char format=GNT_UNKNOWN; // format of number used by us
+
+  /* Checking for format number */
+  while (*IN) {
+    if (length==0 && *IN=='+')
+      format=GNT_INTERNATIONAL;  // first byte is '+'. It can be international
+    else {
+      if (*IN>'9' || *IN<'0') { 
+        format=GNT_ALPHANUMERIC; //char is not number. It must be alphanumeric
+      }
+    }
+    IN++;length++;
+  }
+
+  /* Now we return to first byte */
+  for (j=0;j<length;j++) IN--;
+
+  /* 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.*/
+  *OUT++=format;
+
+  /* The next field is the number. See GSM 03.40 section 9.1.2 */
+  switch (format) {
+    case GNT_ALPHANUMERIC:
+      length=GSM_PackSevenBitsToEight(0, IN, OUT)*2;
+      break;
+
+    case GNT_INTERNATIONAL:
+      length--;
+      EncodeBCD (OUT, IN+1, length, true);
+      break;
+
+    default:
+      EncodeBCD (OUT, IN, length, true);
+      break;
+  }
+
+  if (semioctet) {
+    return length;
+  } else {
+    /* Convert number of semioctets to number of chars */
+    if (length % 2) length++;
+    return length / 2 + 1;
+  }
+}
+
+char *GSM_UnpackSemiOctetNumber(u8 *Number, bool semioctet) {
+
+  static char Buffer[20]="";  
+  int length=Number[0];
+
+  if (semioctet) {
+    /* Convert number of semioctets to number of chars */
+    if (length % 2) length++;
+    length=length / 2 + 1;
+  }
+  
+  length--; //without leading byte with format of number
+
+  switch (Number[1]) {
+    case GNT_ALPHANUMERIC:
+      GSM_UnpackEightBitsToSeven(0, length, length, Number+2, Buffer);
+      Buffer[length]=0;
+      break;
+
+    case GNT_INTERNATIONAL:
+      Buffer[0]='+';
+      DecodeBCD (Buffer+1, Number+2, length);
+      break;
+
+    default:
+      DecodeBCD (Buffer, Number+2, length);
+      break;
+  }
+
+  return Buffer;
+}
+
+/* See GSM 03.40 section 9.2.3.11 */
+GSM_Error GSM_EncodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
+{
+#ifdef DEBUG
+  fprintf(stdout,_("Date & time in saved SMS: %02i/%02i/%04i %02i:%02i:%02i\n"),
+    DT->Day,DT->Month,DT->Year,DT->Hour,DT->Minute,DT->Second);
+#endif
+  
+  req[0]=EncodeWithBCDAlphabet(DT->Year);
+  req[1]=EncodeWithBCDAlphabet(DT->Month);
+  req[2]=EncodeWithBCDAlphabet(DT->Day);
+  req[3]=EncodeWithBCDAlphabet(DT->Hour);
+  req[4]=EncodeWithBCDAlphabet(DT->Minute);
+  req[5]=EncodeWithBCDAlphabet(DT->Second);
+
+  /* FIXME: do it */
+  req[6]=0; /* TimeZone = +-0 */
+
+  return GE_NONE;
+}
+
+/* See GSM 03.40 section 9.2.3.11 */
+GSM_Error GSM_DecodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
+{
+  DT->Year    = DecodeWithBCDAlphabet(req[0]);
+  DT->Month   = DecodeWithBCDAlphabet(req[1]);
+  DT->Day     = DecodeWithBCDAlphabet(req[2]);
+  DT->Hour    = DecodeWithBCDAlphabet(req[3]);
+  DT->Minute  = DecodeWithBCDAlphabet(req[4]);
+  DT->Second  = DecodeWithBCDAlphabet(req[5]);
+
+  DT->Timezone=(10*(req[6]&0x07)+(req[6]>>4))/4;
+  if (req[6]&0x08) DT->Timezone = -DT->Timezone;
+
+#ifdef DEBUG
+  fprintf(stdout, _("%d%d-"), req[0]&0xf, req[0]>>4);
+  fprintf(stdout, _("%d%d-"), req[1]&0xf, req[1]>>4);
+  fprintf(stdout, _("%d%d "), req[2]&0xf, req[2]>>4);
+  fprintf(stdout, _("%d%d:"), req[3]&0xf, req[3]>>4);
+  fprintf(stdout, _("%d%d:"), req[4]&0xf, req[4]>>4);
+  fprintf(stdout, _("%d%d "), req[5]&0xf, req[5]>>4);
+
+  if (req[6]) {
+    if (req[6] & 0x08) fprintf(stdout, "-");
+                  else fprintf(stdout, "+");
+
+    fprintf(stdout, _("%02d00"), (10*(req[6]&0x07)+(req[6]>>4))/4);
+  }
+
+  fprintf(stdout, "\n");
+#endif
+
+  return GE_NONE;
+}
+
+int GSM_EncodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
+{
+  int off,size=0,size2=0,w,i;
+
+  /* off - length of the user data header */
+  off = 0;
+
+  if (SMS->UDHType) {
+    /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */
+    ETSI->firstbyte |= 0x40;
+
+    /* off - length of the user data header */
+    off = 1 + SMS->UDH[0];
+
+    /* we copy the udh */
+    memcpy(ETSI->MessageText, SMS->UDH, off);
+
+//  if (SMS->UDHType==GSM_HangSMS) ETSI->TPDCS=0x08; //Such SMS hangs phone (5110, 5.07),
+                                                     //when saved to Outbox atry try to read it
+                                                    /*from phone's menu*/
+  }
+
+  switch (SMS->Coding) {
+    /* When save SMS to SIM and it's 8 bit SMS,
+       "Edit" is not displayed in phone menu and
+       "Message cannot be displayed here" instead of message text */
+    case GSM_Coding_8bit:
+
+      /* the mask for the 8-bit data */
+      /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
+      ETSI->TPDCS |= 0xf4;
+    
+      memcpy(ETSI->MessageText + off, SMS->MessageText, SMS->Length);
+
+      size2 = size = SMS->Length+off;
+
+      break;
+
+    case GSM_Coding_Default:
+
+      w=(7-off)%7;
+      if (w<0) w=(14-off)%14;
+
+      size = GSM_PackSevenBitsToEight(w, SMS->MessageText, ETSI->MessageText + off);
+      size += off;
+      size2 = (off*8 + w) / 7 + strlen(SMS->MessageText);
+
+      break;
+
+    case GSM_Coding_Unicode:
+
+      /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
+      ETSI->TPDCS |= 0x08;
+
+#ifdef DEBUG
+      fprintf(stdout,_("SMS Length is %i\n"),strlen(SMS->MessageText));
+#endif
+
+      EncodeUnicode (ETSI->MessageText+off,SMS->MessageText,strlen(SMS->MessageText));
+      /* here we code "special" chars */
+      for (i=0;i<strlen(SMS->MessageText);i++) {
+       if (SMS->MessageText[i]=='~') ETSI->MessageText[off+1+i*2]=1; //enables/disables blinking
+        if (SMS->MessageText[i]=='`') ETSI->MessageText[off+1+i*2]=0; //hides rest ot contents
+      }
+
+      size=size2=strlen(SMS->MessageText)*2+off;
+
+      break;
+  }
+
+  /* FIXME: support for compression */
+  /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
+  /* See also GSM 03.42 */
+  /* if (SMS->Compression) ETSI->TPDCS |= 0x20; */
+
+  /* SMS->Length 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 */
+  /* GSM 03.40 section 9.2.3.16 (TP-User-Data-Length) */
+  ETSI->TPUDL = size2;
+
+  return size;
+}
+
+GSM_Error GSM_DecodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
+{
+  int off,w,i,tmp=0;
+  unsigned char output[161];
+  bool UDHOK;
+
+  /* off - length of the user data header */
+  off = 0;
+  
+  SMS->UDHType = GSM_NoUDH;
+
+  if (ETSI->firstbyte & 64) { /* UDH header available */
+
+    off = (ETSI->MessageText[0] + 1); /* Length of UDH header */
+    
+    /* Copy UDH header into SMS->UDH */
+    for (i = 0; i < off; i++) SMS->UDH[i] = ETSI->MessageText[i];
+
+#ifdef DEBUG
+    fprintf(stdout, "   UDH header available (length %i",off);
+#endif
+    
+    SMS->UDHType = GSM_UnknownUDH;
+
+    i=-1;
+    while (true) {
+      i++;
+      if (UDHHeaders[i].UDHType==GSM_NoUDH) break;
+      tmp=UDHHeaders[i].Length;
+      if (tmp==SMS->UDH[0]) { //if length is the same
+
+        if (tmp==0x05) tmp=tmp-2;/*two last bytes can be different for such UDH*/
+        if (tmp==0x0b) tmp=tmp-3;/*three last bytes can be different for such UDH*/
+
+        UDHOK=true;
+        for (w=0;w<tmp;w++) {
+          if (UDHHeaders[i].Text[w]!=SMS->UDH[w+1]) {
+            UDHOK=false;
+            break;
+          }
+        }
+        if (UDHOK) {
+          SMS->UDHType=UDHHeaders[i].UDHType;
+          break;
+        }
+      }
+    }
+
+#ifdef DEBUG
+    switch (SMS->UDHType) {
+      case GSM_ConcatenatedMessages:
+        fprintf(stdout,_(", concatenated (linked) message %d/%d"),SMS->UDH[5],SMS->UDH[4]);break;
+      case GSM_DisableVoice:
+        fprintf(stdout,_(", disables voice indicator"));break;
+      case GSM_EnableVoice:
+        fprintf(stdout,_(", enables voice indicator"));break;
+      case GSM_DisableFax:
+        fprintf(stdout,_(", disables fax indicator"));break;
+      case GSM_EnableFax:
+        fprintf(stdout,_(", enables fax indicator"));break;
+      case GSM_DisableEmail:
+        fprintf(stdout,_(", disables email indicator"));break;
+      case GSM_EnableEmail:
+        fprintf(stdout,_(", enables email indicator"));break;
+      case GSM_VoidSMS:
+        fprintf(stdout,_(", void SMS"));break;
+      case GSM_WAPBookmarkUDH:
+        fprintf(stdout,_(", WAP Bookmark"));break;
+      case GSM_WAPBookmarkUDHLong:
+        fprintf(stdout,_(", WAP Bookmark, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
+      case GSM_WAPSettingsUDH:
+        fprintf(stdout,_(", WAP Settings, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
+      case GSM_RingtoneUDH:
+        fprintf(stdout,_(", ringtone"));break;
+      case GSM_OpLogo:
+        fprintf(stdout,_(", GSM Operator Logo"));break;
+      case GSM_CallerIDLogo:
+        fprintf(stdout,_(", Caller Logo"));break;
+      case GSM_ProfileUDH:
+        fprintf(stdout,_(", Profile SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
+      case GSM_CalendarNoteUDH:
+        fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
+      case GSM_CalendarNoteUDH2:
+        fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
+      case GSM_PhonebookUDH:
+        fprintf(stdout,_(", Phonebook Entry, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
+      default:
+        fprintf(stdout,_(", UNKNOWN"));break;
+    }
+               
+    fprintf(stdout, ")\n");
+
+    hexdump(off,SMS->UDH);
+#endif
+  }
+
+  SMS->Coding = GSM_Coding_Default;
+
+  /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
+  if ((ETSI->TPDCS & 0xf4) == 0xf4) SMS->Coding=GSM_Coding_8bit;
+  if ((ETSI->TPDCS & 0x08) == 0x08) SMS->Coding=GSM_Coding_Unicode;
+
+  switch (SMS->Coding) {
+    case GSM_Coding_Default:
+      w=(7-off)%7;
+      if (w<0) w=(14-off)%14;
+  
+      SMS->Length=ETSI->TPUDL - (off*8 + w) / 7;
+
+      tmp=GSM_UnpackEightBitsToSeven(w,ETSI->TPUDL-off, SMS->Length, ETSI->MessageText+off, output);
+
+#ifdef DEBUG
+      fprintf(stdout, "   7 bit SMS, body is (length %i): ",SMS->Length);
+#endif /* DEBUG */
+
+      DecodeDefault (SMS->MessageText, output, tmp);
+
+#ifdef DEBUG
+      fprintf(stdout, "%s\n",SMS->MessageText);          
+#endif
+
+      break;
+    case GSM_Coding_8bit:
+      SMS->Length=ETSI->TPUDL - off;
+
+      memcpy(SMS->MessageText,ETSI->MessageText+off,SMS->Length);
+
+#ifdef DEBUG
+      fprintf(stdout, "   8 bit SMS, body is (length %i)\n",SMS->Length);
+      hexdump(SMS->Length,SMS->MessageText);
+#endif /* DEBUG */
+
+      break;
+    case GSM_Coding_Unicode:
+      SMS->Length=(ETSI->TPUDL - off) / 2;
+
+#ifdef DEBUG
+      fprintf(stdout, "   7 bit SMS, body is (length %i), Unicode coding: ",SMS->Length);
+      for (i=0; i<SMS->Length;i++) {
+        fprintf(stdout, "[%02x %02x]", ETSI->MessageText[off+i*2] , ETSI->MessageText[off+i*2+1]);
+      }
+      fprintf(stdout, "\n");             
+#endif /* DEBUG */
+
+      /* here we decode "special" chars */
+      for (i=0; i<SMS->Length;i++) {
+        if (ETSI->MessageText[off+i*2]  ==0x00 &&
+           ETSI->MessageText[off+i*2+1]==0x01)
+         ETSI->MessageText[off+i*2+1]='~'; //enables/disables blinking
+        if (ETSI->MessageText[off+i*2]  ==0x00 &&
+            ETSI->MessageText[off+i*2+1]==0x00)
+         ETSI->MessageText[off+i*2+1]='`'; //hides rest ot contents
+      }
+
+      DecodeUnicode (SMS->MessageText, ETSI->MessageText+off, SMS->Length);
+
+      break;
+  }
+
+  return GE_NONE;
+}
+
+GSM_Error GSM_DecodeETSISMSStatusReportData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
+{
+  /* See GSM 03.40 section 9.2.3.11 (TP-Service-Centre-Time-Stamp) */
+#ifdef DEBUG
+  fprintf(stdout, _("   SMSC response date: "));
+#endif
+  GSM_DecodeSMSDateTime(&SMS->SMSCTime, ETSI->SMSCDateTime);
+    
+  if (ETSI->TPStatus < 0x03) {
+    strcpy(SMS->MessageText,_("Delivered"));
+
+#ifdef DEBUG
+    /* more detailed reason only for debug */
+    /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
+    switch (ETSI->TPStatus) {
+      case 0x00: fprintf(stdout, _("   SM received by the SME"));break;
+      case 0x01: fprintf(stdout, _("   SM forwarded by the SC to the SME but the SC is unable to confirm delivery"));break;
+      case 0x02: fprintf(stdout, _("   SM replaced by the SC"));break;
+    }
+#endif /* DEBUG */
+
+    SMS->Length = 10;
+      
+  } else if (ETSI->TPStatus & 0x40) {
+
+    strcpy(SMS->MessageText,_("Failed"));
+
+#ifdef DEBUG
+    /* more detailed reason only for debug */
+    if (ETSI->TPStatus & 0x20) {
+
+      /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
+      fprintf(stdout, _("   Temporary error, SC is not making any more transfer attempts\n"));
+      switch (ETSI->TPStatus) {
+        case 0x60: fprintf(stdout, _("   Congestion"));break;
+        case 0x61: fprintf(stdout, _("   SME busy"));break;
+        case 0x62: fprintf(stdout, _("   No response from SME"));break;
+        case 0x63: fprintf(stdout, _("   Service rejected"));break;
+        case 0x64: fprintf(stdout, _("   Quality of service not available"));break;
+        case 0x65: fprintf(stdout, _("   Error in SME"));break;
+        default  : fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
+      }
+
+    } else {
+
+      /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
+      fprintf(stdout, _("   Permanent error, SC is not making any more transfer attempts\n"));
+      switch (ETSI->TPStatus) {
+        case 0x40: fprintf(stdout, _("   Remote procedure error"));break;
+        case 0x41: fprintf(stdout, _("   Incompatibile destination"));break;
+        case 0x42: fprintf(stdout, _("   Connection rejected by SME"));break;
+        case 0x43: fprintf(stdout, _("   Not obtainable"));break;
+        case 0x44: fprintf(stdout, _("   Quality of service not available"));break;
+        case 0x45: fprintf(stdout, _("   No internetworking available"));break;
+        case 0x46: fprintf(stdout, _("   SM Validity Period Expired"));break;
+        case 0x47: fprintf(stdout, _("   SM deleted by originating SME"));break;
+        case 0x48: fprintf(stdout, _("   SM Deleted by SC Administration"));break;
+        case 0x49: fprintf(stdout, _("   SM does not exist"));break;
+        default  : fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
+      }
+    }
+#endif /* DEBUG */
+      
+      SMS->Length = 6;
+  } else if (ETSI->TPStatus & 0x20) {
+    strcpy(SMS->MessageText,_("Pending"));
+
+#ifdef DEBUG
+    /* more detailed reason only for debug */
+    /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
+    fprintf(stdout, _("   Temporary error, SC still trying to transfer SM\n"));
+    switch (ETSI->TPStatus) {
+      case 0x20: fprintf(stdout, _("   Congestion"));break;
+      case 0x21: fprintf(stdout, _("   SME busy"));break;
+      case 0x22: fprintf(stdout, _("   No response from SME"));break;
+      case 0x23: fprintf(stdout, _("   Service rejected"));break;
+      case 0x24: fprintf(stdout, _("   Quality of service not aviable"));break;
+      case 0x25: fprintf(stdout, _("   Error in SME"));break;
+      default  : fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
+    }
+#endif /* DEBUG */
+    SMS->Length = 7;
+  } else {
+    strcpy(SMS->MessageText,_("Unknown"));
+
+#ifdef DEBUG
+    /* more detailed reason only for debug */
+    fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);
+#endif /* DEBUG */
+    SMS->Length = 8;
+  }
+
+#ifdef DEBUG
+  fprintf(stdout, _("\n"));
+#endif /* DEBUG */
+
+  return GE_NONE;
+}
+
+GSM_Error GSM_EncodeETSISMSSubmitHeader(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
+{
+  GSM_Error error;
+
+  /* First of all we should get SMSC number */
+  if (SMS->MessageCenter.No) {
+    error = GSM->GetSMSCenter(&SMS->MessageCenter);
+    if (error != GE_NONE) return error;
+    SMS->MessageCenter.No = 0;
+  }
+
+#ifdef DEBUG
+  fprintf(stdout, _("Packing SMS to \"%s\" via message center \"%s\"\n"), SMS->Destination, SMS->MessageCenter.Number);
+#endif /* DEBUG */
+
+  ETSI->SMSCNumber[0]=GSM_PackSemiOctetNumber(SMS->MessageCenter.Number, ETSI->SMSCNumber+1, false);
+
+  /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */
+  if (SMS->ReplyViaSameSMSC) ETSI->firstbyte |= 128;
+  
+  /* When save to Outbox with SMS Class, "Edit" is not displayed in phone menu
+     and can forward it to another phone with set class (for example, 0=Flash) */
+  /* Message Class*/
+  /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
+  if (SMS->Class>=0 && SMS->Class<5) ETSI->TPDCS |= (240+SMS->Class);  
+
+  /* When is not set for SMS saved for Inbox, phone displays "Message" instead
+     of number and doesn't display "Details" info */
+  ETSI->Number[0] = GSM_PackSemiOctetNumber(SMS->Destination, ETSI->Number+1, true);
+
+  return GE_NONE;
+}
+
+GSM_Error GSM_DecodeETSISMSHeader(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
+{
+#ifdef DEBUG
+  fprintf(stdout, _("   SMS center number: %s"), GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
+  if (SMS->folder==0 && (ETSI->firstbyte & 128)!=0) //GST_INBOX
+    fprintf(stdout, _(" (centre set for reply)"));
+#endif
+
+  strcpy(SMS->MessageCenter.Number, GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
+
+  SMS->ReplyViaSameSMSC=false;
+  if ((ETSI->firstbyte & 128)!=0) SMS->ReplyViaSameSMSC=true;
+
+#ifdef DEBUG      
+  fprintf(stdout, _("\n   Remote number (recipient or sender): %s\n"), GSM_UnpackSemiOctetNumber(ETSI->Number,true));
+#endif
+
+  strcpy(SMS->Sender, GSM_UnpackSemiOctetNumber(ETSI->Number,true));
+
+  return GE_NONE;
+}
+
+/* FIXME: we should allow for all validity formats */
+GSM_Error GSM_EncodeETSISMSSubmitValidity(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
+{
+  /* GSM 03.40 section 9.2.3.3 (TP-Validity-Period-Format) */
+  /* Bits 4 and 3: 10. TP-VP field present and integer represent (relative) */
+  ETSI->firstbyte |= 0x10;
+
+  /* GSM 03.40 section 9.2.3.12 (TP-Validity Period) */
+  /* 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)
+    ETSI->TPVP = (unsigned char) (SMS->Validity/5)-1;
+
+  /* 30 minutes intervals up to 1 day */
+  else if ((SMS->Validity > 720) && (SMS->Validity <= 1440))
+    ETSI->TPVP = (unsigned char) ((SMS->Validity-720)/30)+143;
+
+  /* 1 day intervals up to 30 days */
+  else if ((SMS->Validity > 1440) && (SMS->Validity <= 43200))
+    ETSI->TPVP = (unsigned char) (SMS->Validity/1440)+166;
+
+  /* 1 week intervals up to 63 weeks */
+  else if ((SMS->Validity > 43200) && (SMS->Validity <= 635040))
+    ETSI->TPVP = (unsigned char) (SMS->Validity/10080)+192;
+
+  return GE_NONE;
+}
+
+/* FIXME: do we need more than SMS_Submit and SMS_Deliver ? */
+GSM_Error GSM_EncodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI, SMS_MessageType PDU, int *length)
+{
+  int size=0;
+
+  ETSI->firstbyte=0;
+  ETSI->TPPID=0;
+  ETSI->TPDCS=0;
+  ETSI->TPUDL=0;
+  ETSI->TPStatus=0;
+  ETSI->TPVP=0;
+
+  switch (PDU) {
+    case SMS_Submit:
+
+      /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
+      /* Bits 1 and 0: 01. SMS-Submit */
+      ETSI->firstbyte |= 0x01;
+
+      /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
+      /* Mask for request for delivery report from SMSC */
+      if (SMS->Type == GST_DR) ETSI->firstbyte |= 32;
+
+      GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
+      GSM_EncodeETSISMSSubmitValidity(SMS, ETSI);
+      size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
+
+      break;
+    case SMS_Deliver:
+
+      /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
+      /* Bits 1 and 0: 00. SMS-Deliver */
+      ETSI->firstbyte |= 0x00;
+
+      GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
+      GSM_EncodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
+      size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
+
+      break;
+    default:
+      break;
+  }
+
+  /* size is the length of the data in octets including udh */
+  *length=size;
+
+  return GE_NONE;
+}
+
+/* This function decodes parts of SMS coded according to GSM 03.40 
+   (given in GSM_ETSISMSMessage) to GSM_SMSMessage */
+GSM_Error GSM_DecodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
+{
+  SMS_MessageType PDU=SMS_Deliver;
+
+  /* See GSM 03.40 section 9.2.3.1 */
+  if ((ETSI->firstbyte & 0x03) == 0x01) PDU=SMS_Submit;
+  if ((ETSI->firstbyte & 0x03) == 0x02) PDU=SMS_Status_Report;
+
+  GSM_DecodeETSISMSHeader(SMS, ETSI);
+
+  switch (PDU) {
+    case SMS_Submit:
+#ifdef DEBUG
+      fprintf(stdout, _("   SMS submit "));
+#endif
+      SMS->SMSData=false; 
+      GSM_DecodeETSISMSSubmitData(SMS,ETSI);
+      break;
+    case SMS_Deliver:
+#ifdef DEBUG
+      fprintf(stdout, _("   SMS deliver "));
+      fprintf(stdout, _("   Date: "));
+#endif
+      SMS->SMSData=true; 
+      GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
+      GSM_DecodeETSISMSSubmitData(SMS,ETSI);
+      break;
+    case SMS_Status_Report:
+#ifdef DEBUG
+      fprintf(stdout, _("   SMS status report "));
+      fprintf(stdout, _("   Date: "));
+#endif
+      SMS->SMSData=true; 
+      GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
+      GSM_DecodeETSISMSStatusReportData(SMS,ETSI);
+      break;
+    default:
+      break;
+  }
+
+  SMS->MessageText[SMS->Length]=0;
+
+  return GE_NONE;
+}
+
+void GSM_SetDefaultSMSData (GSM_SMSMessage *SMS)
+{
+  struct tm *now;
+  time_t nowh;
+  GSM_DateTime Date;
+
+  /* Default settings for SMS message:
+  - no delivery report
+  - Class Message 1
+  - no compression
+  - SMSC no. 1
+  - validity 3 days */
+
+  SMS->folder = GST_OUTBOX;
+  SMS->Type = GST_SMS;
+  SMS->Class = -1;
+  SMS->Compression = false;
+  SMS->MessageCenter.No = 1;
+  SMS->Validity = 4320; /* 4320 minutes == 72 hours */
+  SMS->ReplyViaSameSMSC = false;
+  SMS->UDHType = GSM_NoUDH;
+  SMS->Coding=GSM_Coding_Default;
+  strcpy(SMS->Destination,"");
+
+  /* This part is required to save SMS */    
+
+  SMS->Status = GSS_NOTSENTREAD;
+  SMS->Location = 0;
+
+  nowh=time(NULL);
+  now=localtime(&nowh);
+
+  Date.Year = now->tm_year;
+  Date.Month = now->tm_mon+1;
+  Date.Day = now->tm_mday;
+  Date.Hour = now->tm_hour;
+  Date.Minute = now->tm_min;
+  Date.Second = now->tm_sec;
+
+  /* I have 100 (for 2000) Year now :-) */
+  if (Date.Year>99 && Date.Year<1900) {
+    Date.Year=Date.Year+1900;
+  }
+
+  /* We need to have only two last digits of year */
+  if (Date.Year>1900)
+  {
+    if (Date.Year<2000) Date.Year = Date.Year-1900;
+                   else Date.Year = Date.Year-2000;
+  }
+
+  SMS->Time.Year=Date.Year;
+  SMS->Time.Month=Date.Month;
+  SMS->Time.Day=Date.Day;
+  SMS->Time.Hour=Date.Hour;
+  SMS->Time.Minute=Date.Minute;
+  SMS->Time.Second=Date.Second;
+
+  SMS->Name[0]=0;
+}
+
+/* This function encodes the UserDataHeader as described in:
+   - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
+   - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
+*/
+GSM_Error EncodeUDHHeader(char *text, GSM_UDH UDHType)
+{
+       int i=0;
+
+       if (UDHType!=GSM_NoUDH) {
+          while (true) {
+            if (UDHHeaders[i].UDHType==GSM_NoUDH) {
+#ifdef DEBUG
+               fprintf(stderr,_("Not supported User Data Header type\n"));
+#endif
+               break;
+            }
+            if (UDHHeaders[i].UDHType==UDHType) {
+               text[0] = UDHHeaders[i].Length; // UDH Length
+               memcpy(text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
+               break;
+            }
+            i++;
+          }
+        }
+       return GE_NONE;
+}
+
+int GSM_MakeSinglePartSMS2(GSM_SMSMessage *SMS,
+    unsigned char *MessageBuffer,int cur, GSM_UDH UDHType, GSM_Coding_Type Coding){
+
+  int j;
+  int current,smsudhlength;
+
+  GSM_SetDefaultSMSData(SMS);
+
+  EncodeUDHHeader(SMS->UDH, UDHType);
+  SMS->UDHType=UDHType;
+
+  switch (UDHType) {
+    case GSM_EnableVoice:
+    case GSM_DisableVoice:
+    case GSM_EnableEmail:
+    case GSM_DisableEmail:
+    case GSM_EnableFax:
+    case GSM_DisableFax:
+      SMS->Class=1;
+      SMS->Coding=Coding;
+      break;
+    case GSM_NoUDH:
+    case GSM_ConcatenatedMessages:
+    case GSM_VoidSMS:
+    case GSM_HangSMS:
+    case GSM_BugSMS:
+    case GSM_PhonebookUDH:
+    case GSM_CalendarNoteUDH: //class=1?
+      SMS->Class=-1;
+      SMS->Coding=Coding;
+      break;
+    case GSM_OpLogo:
+    case GSM_CallerIDLogo:
+    case GSM_RingtoneUDH:
+    case GSM_WAPBookmarkUDH:
+    case GSM_WAPBookmarkUDHLong:
+    case GSM_WAPSettingsUDH:
+    case GSM_ProfileUDH:
+      SMS->Class=1;
+      SMS->Coding=GSM_Coding_8bit;
+      break;
+    default:
+      fprintf(stderr,_("Error in makesinglepartsms !\n\n\n"));
+  }
+
+  current=cur;
+
+  smsudhlength=0;
+  if (UDHType!=GSM_NoUDH)
+    smsudhlength=SMS->UDH[0]+1;
+
+  j=0;
+  switch (SMS->Coding) {
+    case GSM_Coding_8bit:
+      j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength);     //max=140
+      break;
+    case GSM_Coding_Default:
+      j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)*8/7; //max=160
+      break;
+    case GSM_Coding_Unicode:
+      j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)/2;   //max=70
+      break;
+  }
+  if (current>j) current=j;
+
+  memcpy(SMS->MessageText,MessageBuffer,current);    
+  SMS->MessageText[current]=0;
+  SMS->Length=current;
+
+  return current;
+}
+
+void GSM_MakeMultiPartSMS2(GSM_MultiSMSMessage *SMS,
+    unsigned char *MessageBuffer,int MessageLength, GSM_UDH UDHType, GSM_Coding_Type Coding){
+
+  int i=0,j,pos=0,current=0;
+
+  for (i=0;i<4;i++) {  
+    if (pos==MessageLength) break;
+
+    current=MessageLength-pos;
+
+    pos=pos+GSM_MakeSinglePartSMS2(&SMS->SMS[i],MessageBuffer+pos,current,UDHType,Coding);
+  }
+
+  for (j=0;j<i;j++)
+  {
+    switch (UDHType) {
+      case GSM_ProfileUDH:
+      case GSM_WAPBookmarkUDHLong:
+      case GSM_WAPSettingsUDH:
+      case GSM_CalendarNoteUDH:
+      case GSM_CalendarNoteUDH2:
+      case GSM_PhonebookUDH:
+        SMS->SMS[j].UDH[10]=i;
+        SMS->SMS[j].UDH[11]=j+1;
+        break;
+      case GSM_ConcatenatedMessages:
+        SMS->SMS[j].UDH[4]=i;
+        SMS->SMS[j].UDH[5]=j+1;
+        break;
+    default:
+      break;
+    }
+  }
+
+  SMS->number=i;
+}
diff --git a/common/misc.c b/common/misc.c
new file mode 100644 (file)
index 0000000..34d81b7
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+*/
+
+#include <string.h>\r
+#include <ctype.h>\r
+#include <time.h>\r
+
+#ifndef WIN32
+  #include <sys/types.h>
+  #include <sys/stat.h>
+  #include <stdlib.h>
+  #include <fcntl.h>
+  #include <signal.h>
+  #include <unistd.h>
+  #include <errno.h>
+#endif\r
+\r
+#include "misc.h"\r
+#include "gsm-common.h"\r
+
+#ifndef HAVE_TIMEOPS
+
+/* FIXME: I have timersub defined in sys/time.h :-( PJ
+   FIXME: Jano wants this function too... PJ
+
+int timersub(struct timeval *a, struct timeval *b, struct timeval *result) {
+  do {
+    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;
+    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;
+    if ((result)->tv_usec < 0) {
+      --(result)->tv_sec;
+      (result)->tv_usec += 1000000;
+    }
+  } while (0);
+}
+*/
+
+#endif
+
+int GetLine(FILE *File, char *Line, int count) {
+
+  char *ptr;
+
+  if (fgets(Line, count, File)) {
+    ptr=Line+strlen(Line)-1;
+
+    while ( (*ptr == '\n' || *ptr == '\r') && ptr>=Line) *ptr--='\0';
+
+    return strlen(Line);
+  } else return -1;
+}
+
+/*
+ * like atoi, but of a non-null-terminated string of a specified portion
+ */
+int mem_to_int(const char str[], int len)
+{
+  char aux[81];
+
+  strncpy(aux, str, len);
+  aux[len]=0;
+  return( atoi(aux) );
+} 
+
+/*
+ * make hexdump of Message
+ */
+#ifdef DEBUG
+void hexdump(u16 MessageLength, u8 *MessageBuffer)
+{
+  int count;
+  int n=0;
+  char string1[80]="";
+  char string2[80]="";
+  char hex1[10];
+  char hex2[10];
+  for (count = 0; count < MessageLength; count ++)
+  {
+    n++;
+
+    switch (MessageBuffer[count]) {
+      case 0x09:
+        sprintf(hex1,"%02x  ",MessageBuffer[count]);
+        strcpy(hex2,".");
+        break;
+      default:
+        if (isprint(MessageBuffer[count]))
+          sprintf(hex1,"%02x%c ",MessageBuffer[count],MessageBuffer[count]);
+        else
+          sprintf(hex1,"%02x  ",MessageBuffer[count]);
+
+        if (isprint(MessageBuffer[count])) sprintf(hex2,"%c",MessageBuffer[count]);
+                                      else strcpy(hex2,".");
+        break;
+    }
+
+    if ( n!=15 && count != MessageLength-1 ) hex1[3]='|';
+    strcat(string1,hex1);
+    strcat(string2,hex2);
+    if ( n==15 || count == MessageLength-1 )
+    {      
+      fprintf(stdout,"%-60s%03x %s\n",string1,count+1,string2);
+      strcpy(string1,"");
+      strcpy(string2,"");
+      n=0;
+    }
+  }//for count
+
+  if (n!=0) fprintf (stdout,_("\n")); 
+  fflush(stdout);
+}
+
+void txhexdump(u16 MessageLength, u8 *MessageBuffer)
+{ 
+  int count;
+  int n=0;
+  for (count = 0; count < MessageLength; count ++)
+   {
+    n++;
+    fprintf(stdout,_("%02x"),MessageBuffer[count]);
+    switch (MessageBuffer[count]) {
+      case 0x09:
+        fprintf(stdout,_(" |"));
+        break;
+      default:
+        if (isprint(MessageBuffer[count])) fprintf(stdout, _("%c|"),MessageBuffer[count]);
+                                      else fprintf(stdout,_(" |"));
+        break;
+    }
+
+    if (n==18)
+    { 
+      fprintf (stdout,_("\n"));
+      n=0;
+    }
+   }//for count
+
+  if (n!=0) fprintf (stdout,_("\n")); 
+
+  fflush(stdout);
+}
+#endif
+
+#ifndef WIN32
+
+#define max_buf_len 128
+#define lock_path "/var/lock/LCK.."
+
+/* Lock the device. Return allocated string with a lock name */
+char *lock_device(const char* port)
+{
+       char *lock_file = NULL;
+       char buffer[max_buf_len];
+       char *aux = rindex(port, '/');
+       int fd, len = strlen(aux) + strlen(lock_path);
+
+       memset(buffer, 0, sizeof(buffer));
+       lock_file = calloc(len + 1, 1);
+       if (!lock_file) {
+               fprintf(stderr, _("Cannot lock device\n"));
+               return NULL;
+       }
+       /* I think we don't need to use strncpy, as we should have enough