This commit was manufactured by cvs2svn to create branch 'decode'.
authorjankratochvil <>
Thu, 28 Feb 2002 02:40:36 +0000 (02:40 +0000)
committerjankratochvil <>
Thu, 28 Feb 2002 02:40:36 +0000 (02:40 +0000)
Cherrypick from uc 2002-02-18 02:07:04 UTC jankratochvil 'This commit was manufactured by cvs2svn to create branch 'uc'.':
    common/devices/unixserial.c
    common/files/cfgreader.c
    include/config.h.in.in
    include/protocol/fbus.h
Cherrypick from ats 2002-02-28 02:40:34 UTC short '\r\n -> \n':
    common/misc.c
    gnokii/gnokii.c
Cherrypick from mygnokii 2002-02-28 02:40:35 UTC short '\r\n -> \n':
    common/newmodules/n6110.c
    common/protocol/fbus.c

common/devices/unixserial.c [new file with mode: 0644]
common/files/cfgreader.c [new file with mode: 0644]
common/misc.c [new file with mode: 0644]
common/newmodules/n6110.c [new file with mode: 0644]
common/protocol/fbus.c [new file with mode: 0644]
gnokii/gnokii.c [new file with mode: 0644]
include/config.h.in.in [new file with mode: 0644]
include/protocol/fbus.h [new file with mode: 0644]

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/files/cfgreader.c b/common/files/cfgreader.c
new file mode 100644 (file)
index 0000000..9908cc1
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+
+  $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.
+
+  Config file (/etc/gnokiirc and ~/.gnokiirc) reader.
+
+  Modified from code by Tim Potter.
+
+*/
+
+#include "misc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if __unices__
+#  include <strings.h>
+#endif
+#include <ctype.h>
+#include <errno.h>
+
+#include "files/cfgreader.h"
+
+/* Read configuration information from a ".INI" style file */
+struct CFG_Header *CFG_ReadFile(char *filename)
+{
+        FILE *handle;
+        char *line;
+        char *buf;
+        struct CFG_Header *cfg_info = NULL, *cfg_head = NULL;
+
+        /* Error check */
+        if (filename == NULL) {
+                return NULL;
+        }
+
+        /* Initialisation */
+        if ((buf = (char *)malloc(255)) == NULL) {
+                return NULL;
+        }
+    
+        /* Open file */
+        if ((handle = fopen(filename, "r")) == NULL) {
+#ifdef DEBUG
+                fprintf( stderr, "CFG_ReadFile - open %s: %s\n", filename, strerror(errno));
+#endif /* DEBUG */
+                return NULL;
+        }
+#ifdef DEBUG
+        else
+                fprintf( stderr, "Opened configuration file %s\n", filename );
+#endif /* DEBUG */
+
+        /* Iterate over lines in the file */
+        while (fgets(buf, 255, handle) != NULL) {
+
+                line = buf;
+
+                /* Strip leading, trailing whitespace */
+                while(isspace((int) *line))
+                        line++;
+
+                while((strlen(line) > 0) && isspace((int) line[strlen(line) - 1]))
+                        line[strlen(line) - 1] = '\0';
+        
+                /* Ignore blank lines and comments */
+                if ((*line == '\n') || (*line == '\0') || (*line == '#'))
+                        continue;
+
+                /* Look for "headings" enclosed in square brackets */
+                if ((line[0] == '[') && (line[strlen(line) - 1] == ']')) {
+                        struct CFG_Header *heading;
+
+                        /* Allocate new heading entry */
+                        if ((heading = (struct CFG_Header *)malloc(sizeof(*heading))) == NULL) {
+                                return NULL;
+                        }
+
+                        /* Fill in fields */
+                        memset(heading, '\0', sizeof(*heading));
+            
+                        line++;
+                        line[strlen(line) - 1] = '\0';
+
+                        /* FIXME: strdup is not ANSI C compliant. */
+                        heading->section = strdup(line);
+
+                        /* Add to tail of list  */
+                        heading->prev = cfg_info;
+
+                        if (cfg_info != NULL) {
+                                cfg_info->next = heading;
+                        } else {
+                                /* Store copy of head of list for return value */
+                                cfg_head = heading;
+                        }
+
+                        cfg_info = heading;
+
+#ifdef DEBUG
+                        fprintf(stderr, "Added new section %s\n", heading->section);
+#endif
+                        /* Go on to next line */
+
+                        continue;
+                }
+
+                /* Process key/value line */
+
+                if ((strchr(line, '=') != NULL) && cfg_info != NULL) {
+                        struct CFG_Entry *entry;
+                        char *value;
+
+                        /* Allocate new entry */
+                        if ((entry = (struct CFG_Entry *)malloc(sizeof(*entry))) == NULL) {
+                                return NULL;
+                        }
+
+                        /* Fill in fields */
+                        memset(entry, '\0', sizeof(*entry));
+
+                        value = strchr(line, '=');
+                        *value = '\0';                /* Split string */
+                        value++;
+            
+                        while(isspace((int) *value)) {      /* Remove leading white */
+                                value++;
+                        }
+
+                        entry->value = strdup(value);
+
+                        while((strlen(line) > 0) && isspace((int) line[strlen(line) - 1])) {
+                                line[strlen(line) - 1] = '\0';  /* Remove trailing white */
+                        }
+
+                        /* FIXME: strdup is not ANSI C compliant. */
+                        entry->key = strdup(line);
+
+                        /* Add to head of list */
+
+                        entry->next = cfg_info->entries;
+
+                        if (cfg_info->entries != NULL) {
+                                cfg_info->entries->prev = entry;
+                        }
+
+                        cfg_info->entries = entry;
+
+#ifdef DEBUG
+                        fprintf(stderr, "Adding key/value %s/%s\n", entry->key, entry->value);
+#endif
+                        /* Go on to next line */
+                        continue;
+                }
+
+                        /* Line not part of any heading */
+                fprintf(stderr, "Orphaned line: %s\n", line);
+        }
+
+        /* Return pointer to configuration information */
+        return cfg_head;
+}
+
+/*  Write configuration information to a config file */
+
+int CFG_WriteFile(struct CFG_Header *cfg, char *filename)
+{
+  /* Not implemented - tricky to do and preserve comments */
+
+  return 0;
+}
+
+/* 
+ * Find the value of a key in a config file.  Return value associated
+ * with key or NULL if no such key exists. 
+ */
+
+char *CFG_Get(struct CFG_Header *cfg, char *section, char *key)
+{
+        struct CFG_Header *h;
+        struct CFG_Entry *e;
+
+        if ((cfg == NULL) || (section == NULL) || (key == NULL)) {
+                return NULL;
+        }
+
+        /* Search for section name */
+        for (h = cfg; h != NULL; h = h->next) {
+                if (strcmp(section, h->section) == 0) {
+                        /* Search for key within section */
+                        for (e = h->entries; e != NULL; e = e->next) {
+                                if (strcmp(key, e->key) == 0) {
+                                        /* Found! */
+                                        return e->value;
+                                }
+                        }
+                }
+        }
+        /* Key not found in section */
+        return NULL;
+}
+
+/*  Set the value of a key in a config file.  Return the new value if
+    the section/key can be found, else return NULL.  */
+
+char *CFG_Set(struct CFG_Header *cfg, char *section, char *key, 
+                    char *value)
+{
+        struct CFG_Header *h;
+        struct CFG_Entry *e;
+
+        if ((cfg == NULL) || (section == NULL) || (key == NULL) || 
+            (value == NULL)) {
+                return NULL;
+        }
+
+        /* Search for section name */
+        for (h = cfg; h != NULL; h = h->next) {
+                if (strcmp(section, h->section) == 0) {
+                        /* Search for key within section */
+                        for (e = h->entries; e != NULL; e = e->next) {
+                                if ((e->key != NULL) && strcmp(key, e->key) == 0) {
+                                        /* Found - set value */
+                                        free(e->key);
+                                        /* FIXME: strdup is not ANSI C compliant. */
+                                        e->key = strdup(value);
+                                        return e->value;
+                                }
+                        }
+                }
+        }
+        /* Key not found in section */
+        return NULL;    
+}
+
+struct CFG_Header *CFG_FindGnokiirc()
+{
+       struct CFG_Header *cfg_info;
+        char *homedir;
+        char rcfile[200];
+
+#ifdef WIN32
+        homedir = getenv("HOMEDRIVE");
+        strncpy(rcfile, homedir ? homedir : "", 200);
+        homedir = getenv("HOMEPATH");
+        strncat(rcfile, homedir ? homedir : "", 200);
+        strncat(rcfile, "\gnokiirc", 200);
+#else
+        homedir = getenv("HOME");
+        if (homedir) strncpy(rcfile, homedir, 200);
+        strncat(rcfile, "/.gnokiirc", 200);
+#endif
+
+        /* Try opening .gnokirc from users home directory first */
+        if ((cfg_info = CFG_ReadFile(rcfile)) == NULL) {
+#ifndef WIN32
+
+                /* It failed so try for /etc/gnokiirc */
+                if ((cfg_info = CFG_ReadFile("/etc/gnokiirc")) == NULL) {
+                        /* That failed too so exit */
+#ifdef DEBUG
+                        fprintf(stderr, _("Couldn't open %s or /etc/gnokiirc. Using defaults...\n"), rcfile);
+#endif /* DEBUG */
+                        return NULL;
+                }
+
+#else /* WIN32 */
+
+                /* It failed so try for gnokiirc */
+                if ((cfg_info = CFG_ReadFile("gnokiirc")) == NULL) {
+                        /* That failed too so exit */
+#ifdef DEBUG
+                        fprintf(stderr, _("Couldn't open %s or gnokiirc. Using defaults...\n"), rcfile);
+#endif /* DEBUG */
+                        return NULL;
+                }
+
+#endif /* WIN32 */
+        }
+
+       return cfg_info;
+}
+
+int CFG_ReadConfig(char **model, char **port, char **initlength,
+               char **connection, char **bindir, char **synchronizetime,
+              bool isgnokiid)
+{
+        struct CFG_Header *cfg_info;
+#ifdef WIN32
+        char *DefaultPort            = "com2:";
+#else
+        char *DefaultPort            = "/dev/ttyS1";
+#endif
+        char *DefaultModel           = "auto";
+        char *DefaultConnection      = "fbus";
+        char *DefaultBindir          = "/usr/local/sbin/";
+       char *DefaultSynchronizeTime = "yes";
+       char *DefaultInitLength      = "30";
+
+       char *section = "global";
+
+        (char *)*model = DefaultModel;
+        (char *)*port = DefaultPort;
+        (char *)*connection = DefaultConnection;
+        (char *)*bindir = DefaultBindir;
+        (char *)*synchronizetime = DefaultSynchronizeTime;
+        (char *)*initlength = DefaultInitLength;
+       
+       cfg_info=CFG_FindGnokiirc();
+       if (cfg_info==NULL) return 0;
+
+       if (isgnokiid) (char *)section = "gnokiid";
+       
+        (char *)*model = CFG_Get(cfg_info, section, "model");
+        if (!*model) (char *)*model = DefaultModel;
+
+        (char *)*port = CFG_Get(cfg_info, section, "port");
+        if (!*port) (char *)*port = DefaultPort;
+
+        (char *)*connection = CFG_Get(cfg_info, section, "connection");
+        if (!*connection) (char *)*connection = DefaultConnection;
+
+        (char *)*bindir = CFG_Get(cfg_info, section, "bindir");
+        if (!*bindir) (char *)*bindir = DefaultBindir;
+
+        (char *)*synchronizetime = CFG_Get(cfg_info, section, "synchronizetime");
+        if (!*synchronizetime) (char *)*synchronizetime = DefaultSynchronizeTime;
+
+        (char *)*initlength = CFG_Get(cfg_info, section, "initlength");
+        if (!*initlength) (char *)*initlength = "default";
+
+        return 0;
+}
diff --git a/common/misc.c b/common/misc.c
new file mode 100644 (file)
index 0000000..b17a93e
--- /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>
+#include <ctype.h>
+#include <time.h>
+
+#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
+
+#include "misc.h"
+#include "gsm-common.h"
+
+#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
+        * buffer due to strlen results
+        */
+       strcpy(lock_file, lock_path);
+       strcat(lock_file, aux);
+
+       /* Check for the stale lockfile.
+        * The code taken from minicom by Miquel van Smoorenburg */
+       if ((fd = open(lock_file, O_RDONLY)) >= 0) {
+               char buf[max_buf_len];
+               int pid, n = 0;
+
+               n = read(fd, buf, sizeof(buf) - 1);
+               close(fd);
+               if (n > 0) {
+                       pid = -1;
+                       if (n == 4)
+                               /* Kermit-style lockfile. */
+                               pid = *(int *)buf;
+                       else {
+                               /* Ascii lockfile. */
+                               buf[n] = 0;
+                               sscanf(buf, "%d", &pid);
+                       }
+                       if (pid > 0 && kill((pid_t)pid, 0) < 0 && errno == ESRCH) {
+                               fprintf(stderr, _("Lockfile is stale. Overriding it..\n"));
+                               sleep(1);
+                               unlink(lock_file);
+                       } else
+                               n = 0;
+               }
+               if (n == 0) {
+                       free(lock_file);
+                       fprintf(stderr, _("Device is already locked.\n"));
+                       return NULL;
+               }
+       }
+
+       /* Try to create a new file, with 0644 mode */
+       fd = open(lock_file, O_CREAT | O_EXCL, 0644);
+       if (fd == -1) {
+               free(lock_file);
+               fprintf(stderr, _("Cannot lock device\n"));
+               return NULL;
+       }
+       sprintf(buffer, "%10ld gnokii\n", (long)getpid());
+       write(fd, buffer, strlen(buffer));
+       close(fd);
+       return lock_file;
+}
+
+/* Removes lock and frees memory */
+bool unlock_device(char *lock_file)
+{
+       int err;
+
+       if (!lock_file) {
+               fprintf(stderr, _("Cannot unlock device\n"));
+               return false;
+       }
+       err = unlink(lock_file);
+       free(lock_file);
+       return (err + 1);
+}
+#endif /* WIN32 */
diff --git a/common/newmodules/n6110.c b/common/newmodules/n6110.c
new file mode 100644 (file)
index 0000000..324b401
--- /dev/null
@@ -0,0 +1,5645 @@
+/*
+
+  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 an API for accessing functions on the 6110 and similar
+  phones.
+
+*/
+
+/* "Turn on" prototypes in n-6110.h */
+
+#define __n_6110_c 
+
+/* System header files */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef WIN32
+  #include "misc_win32.h"
+#endif
+/* Various header file */
+#ifndef VC6
+  #include "config.h"
+#endif
+
+#include "gsm-api.h"
+#include "gsm-coding.h"
+#include "newmodules/n6110.h"
+#include "newmodules/n7110.h"
+#include "protocol/fbus.h"
+#include "devices/device.h"
+/* Global variables used by code in gsm-api.c to expose the functions
+   supported by this model of phone. */
+
+
+
+
+
+
+
+/* Here we initialise model specific functions. */
+GSM_Functions N6110_Functions = {
+  N6110_Initialise,
+  N6110_DispatchMessage,
+  NULL_Terminate,
+  NULL_KeepAlive,
+  N6110_GetMemoryLocation,
+  N6110_WritePhonebookLocation,
+  N6110_GetSpeedDial,
+  N6110_SetSpeedDial,
+  N6110_GetMemoryStatus,
+  N6110_GetSMSStatus,
+  N6110_GetSMSCenter,
+  N6110_SetSMSCenter,
+  N6110_GetSMSMessage,
+  N6110_DeleteSMSMessage,
+  N6110_SendSMSMessage,
+  N6110_SaveSMSMessage,
+  N6110_GetRFLevel,
+  N6110_GetBatteryLevel,
+  N6110_GetPowerSource,
+  N6110_GetDisplayStatus,
+  N6110_EnterSecurityCode,
+  N6110_GetSecurityCodeStatus,
+  N6110_GetSecurityCode,
+  N6110_GetIMEI,
+  N6110_GetRevision,
+  N6110_GetModel,
+  N6110_GetDateTime,
+  N6110_SetDateTime,
+  N6110_GetAlarm,
+  N6110_SetAlarm,
+  N6110_DialVoice,
+  N6110_DialData,
+  N6110_GetIncomingCallNr,
+  N6110_GetNetworkInfo,
+  N6110_GetCalendarNote,
+  N6110_WriteCalendarNote,
+  N6110_DeleteCalendarNote,
+  N6110_NetMonitor,
+  N6110_SendDTMF,
+  N6110_GetBitmap,
+  N6110_SetBitmap,
+  N6110_SetRingTone,
+  N6110_SetBinRingTone,
+  N6110_GetBinRingTone,
+  N6110_Reset,
+  N6110_GetProfile,
+  N6110_SetProfile,
+  N6110_SendRLPFrame,
+  N6110_CancelCall,
+  N6110_PressKey,
+  N6110_EnableDisplayOutput,
+  N6110_DisableDisplayOutput,
+  N6110_EnableCellBroadcast,
+  N6110_DisableCellBroadcast,
+  N6110_ReadCellBroadcast,
+  N6110_PlayTone,
+  N6110_GetProductProfileSetting,
+  N6110_SetProductProfileSetting,
+  N6110_GetOperatorName,
+  N6110_SetOperatorName,
+  N6110_GetVoiceMailbox,  N6110_Tests,
+  N6110_SimlockInfo,
+  UNIMPLEMENTED,                 //GetCalendarNotesInfo
+  N6110_GetSMSFolders,
+  N6110_ResetPhoneSettings,
+  N7110_GetWAPBookmark,
+  N7110_SetWAPBookmark,
+  N7110_GetWAPSettings,
+  N6110_CallDivert,
+  N6110_AnswerCall,
+  N6110_GetManufacturer
+};
+
+/* Mobile phone information */
+
+GSM_Information N6110_Information = {
+  "3210|3310|3330|5110|5130|5190|6110|6130|6150|6190|8210|8850",
+     /* Supported models in FBUS */
+  "3210|3310|3330|5110|5130|5190|6110|6130|6150|6190|8210|8850",
+     /* Supported models in MBUS */
+  "6110|6130|6150|8210|8850",
+     /* Supported models in FBUS over infrared */
+  "",
+     /* Supported models in FBUS over DLR3 */
+  "",
+     /* AT commands */
+  "8210|8850",
+     /* infrared sockets */
+  "6110|6130|6150|8210|8850",
+     /* Supported models in FBUS over infrared with Tekram device */  
+  4,                     /* Max RF Level */
+  0,                     /* Min RF Level */
+  GRF_Arbitrary,         /* RF level units */
+  4,                     /* Max Battery Level */
+  0,                     /* Min Battery Level */
+  GBU_Arbitrary,         /* Battery level units */
+  GDT_DateTime,          /* Have date/time support */
+  GDT_TimeOnly,                 /* Alarm supports time only */
+  1                      /* Only one alarm available */
+};
+
+const char *N6110_MemoryType_String [] = {
+  "",  /* 0x00 */
+  "MT", /* 0x01 */
+  "ME", /* 0x02 */
+  "SM", /* 0x03 */
+  "FD", /* 0x04 */
+  "ON", /* 0x05 */
+  "EN", /* 0x06 */
+  "DC", /* 0x07 */
+  "RC", /* 0x08 */
+  "MC", /* 0x09 */
+};
+
+/* Magic bytes from the phone. */
+unsigned char MagicBytes[4] = { 0x00, 0x00, 0x00, 0x00 };
+
+/* For DisplayOutput */
+char               PhoneScreen[5+1][27+1];
+int                OldX=1000,OldY=0,NewX=0,NewY=0;
+
+void N6110_ReplyEnableExtendedCommands(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Answer for EnableExtendedSecurityCommands frame, meaning not known :-(\n"));
+#endif /* DEBUG */
+
+  CurrentEnableExtendedCommandsError=GE_NONE;  
+}
+
+/* If you set make some things (for example,
+   change Security Code from phone's menu, disable and enable
+   phone), it won't answer for 0x40 frame - you won't be able
+   to play tones, get netmonitor, etc.
+
+   This function do thing called "Enabling extended security commands" -
+   it enables 0x40 frame functions.
+
+   This frame can also some other things - see below */
+GSM_Error N6110_EnableExtendedCommands (unsigned char status)
+{
+  unsigned char req[4] = { 0x00,
+                           0x01,0x64, /* Enable extended commands request */
+                          0x01 };    /* 0x01 - on, 0x00 - off,
+                                        0x03 & 0x04 - soft & hard reset,
+                                         0x06 - CONTACT SERVICE */
+
+  /* 0x06 MAKES CONTACT SERVICE! BE CAREFULL! */
+  /* When use 0x03 and had during session changed time & date
+     some phones (like 6150 or 6210) can ask for time & date after reset
+     or disable clock on the screen */
+  if (status!=0x06) req[3] = status;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentEnableExtendedCommandsError, 4, 0x40, req);
+}
+
+void N6110_ReplyIMEI(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#if defined WIN32 || !defined HAVE_SNPRINTF
+  sprintf(Current_IMEI, "%s", MessageBuffer+4);
+#else
+  snprintf(Current_IMEI, GSM_MAX_IMEI_LENGTH, "%s", MessageBuffer+4);
+#endif
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: IMEI %s received\n"),Current_IMEI);
+#endif
+
+  CurrentGetIMEIError=GE_NONE;       
+}
+
+GSM_Error N6110_SendIMEIFrame()
+{
+  unsigned char req[4] = {0x00, 0x01, 0x66, 0x00};  
+
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+  
+  return NULL_SendMessageSequence (20, &CurrentGetIMEIError, 4, 0x40, req);
+}
+
+void N6110_ReplyHW(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i, j;
+    
+  if (MessageBuffer[3]==0x05) {
+
+#ifdef DEBUG
+    fprintf(stdout,_("Message: Hardware version received: "));
+#endif
+
+    j=strlen(Current_Revision);
+    Current_Revision[j++]=',';
+    Current_Revision[j++]=' ';
+    Current_Revision[j++]='H';
+    Current_Revision[j++]='W';
+            
+    for (i=5;i<9;i++) {
+#ifdef DEBUG
+      fprintf(stdout,_("%c"), MessageBuffer[i]);
+#endif
+      Current_Revision[j++]=MessageBuffer[i];
+    }
+
+#ifdef DEBUG
+    fprintf(stdout,_("\n"));
+#endif
+
+    CurrentGetHWError=GE_NONE;
+  }
+}
+
+GSM_Error N6110_SendHWFrame()
+{
+  unsigned char req[4] = {0x00, 0x01, 0xc8, 0x05};  
+
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+  
+  return NULL_SendMessageSequence (20, &CurrentGetHWError, 4, 0x40, req);
+}
+
+void N6110_ReplyID(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i, j;
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Mobile phone model identification received:\n"));
+  fprintf(stdout, _("   Firmware: "));
+#endif
+
+  strcpy(Current_Revision,"SW");
+    
+  i=6;j=2;
+  while (MessageBuffer[i]!=0x0a) {
+    Current_Revision[j]=MessageBuffer[i];
+#ifdef DEBUG
+    fprintf(stdout, _("%c"),MessageBuffer[i]);
+#endif
+    if (j==GSM_MAX_REVISION_LENGTH-1) {
+#ifdef DEBUG
+      fprintf(stderr,_("ERROR: increase GSM_MAX_REVISION_LENGTH!\n"));
+#endif  
+      break;
+    }
+    j++;
+    i++;
+  }
+  Current_Revision[j+1]=0;
+
+#ifdef DEBUG
+  fprintf(stdout, _("\n   Firmware date: "));
+#endif
+
+  i++;
+  while (MessageBuffer[i]!=0x0a) {
+#ifdef DEBUG
+    fprintf(stdout, _("%c"),MessageBuffer[i]);
+#endif
+    i++;
+  }
+
+#ifdef DEBUG
+  fprintf(stdout, _("\n   Model: "));
+#endif /* DEBUG */
+
+  i++;j=0;
+  while (MessageBuffer[i]!=0x0a) {
+    Current_Model[j]=MessageBuffer[i];
+#ifdef DEBUG
+    fprintf(stdout, _("%c"),MessageBuffer[i]);
+#endif
+    if (j==GSM_MAX_MODEL_LENGTH-1) {
+#ifdef DEBUG
+      fprintf(stderr,_("ERROR: increase GSM_MAX_MODEL_LENGTH!\n"));
+#endif  
+      break;
+    }
+    j++;
+    i++;
+  }
+  Current_Model[j+1]=0;
+
+#ifdef DEBUG
+  fprintf(stdout, _("\n"));
+#endif /* DEBUG */
+    
+  CurrentMagicError=GE_NONE;
+}
+
+GSM_Error N6110_SendIDFrame()
+{
+  unsigned char req[5] = {N6110_FRAME_HEADER, 0x03, 0x00};  
+
+  return NULL_SendMessageSequence (50, &CurrentMagicError, 5, 0xd1, req);
+}
+
+/* This function send the status request to the phone. */
+/* Seems to be ignored in N3210 */
+GSM_Error N6110_SendStatusRequest(void)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x01};
+
+  Protocol->SendMessage(4, 0x04, req);
+
+  return (GE_NONE);
+}
+
+void N6110_ReplyGetAuthentication(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#if defined WIN32 || !defined HAVE_SNPRINTF
+  sprintf(Current_IMEI, "%s", MessageBuffer+9);
+  sprintf(Current_Model, "%s", MessageBuffer+25);
+  sprintf(Current_Revision, "SW%s, HW%s", MessageBuffer+44, MessageBuffer+39);
+#else
+  snprintf(Current_IMEI, GSM_MAX_IMEI_LENGTH, "%s", MessageBuffer+9);
+  snprintf(Current_Model, GSM_MAX_MODEL_LENGTH, "%s", MessageBuffer+25);
+  snprintf(Current_Revision, GSM_MAX_REVISION_LENGTH, "SW%s, HW%s", MessageBuffer+44, MessageBuffer+39);
+#endif
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Mobile phone identification received:\n"));
+  fprintf(stdout, _("   IMEI: %s\n"), Current_IMEI);
+  fprintf(stdout, _("   Model: %s\n"), Current_Model);
+  fprintf(stdout, _("   Production Code: %s\n"), MessageBuffer+31);
+  fprintf(stdout, _("   HW: %s\n"), MessageBuffer+39);
+  fprintf(stdout, _("   Firmware: %s\n"), MessageBuffer+44);
+
+  /* These bytes are probably the source of the "Accessory not connected"
+     messages on the phone when trying to emulate NCDS... I hope....
+     UPDATE: of course, now we have the authentication algorithm. */
+  fprintf(stdout, _("   Magic bytes: %02x %02x %02x %02x\n"), MessageBuffer[50], MessageBuffer[51], MessageBuffer[52], MessageBuffer[53]);
+#endif /* DEBUG */
+
+  MagicBytes[0]=MessageBuffer[50];
+  MagicBytes[1]=MessageBuffer[51];
+  MagicBytes[2]=MessageBuffer[52];
+  MagicBytes[3]=MessageBuffer[53];
+
+  CurrentMagicError=GE_NONE;
+}
+
+/* This function provides Nokia authentication protocol.
+
+   This code is written specially for gnokii project by Odinokov Serge.
+   If you have some special requests for Serge just write him to
+   apskaita@post.omnitel.net or serge@takas.lt
+
+   Reimplemented in C by Pavel Janík ml.
+
+   Nokia authentication protocol is used in the communication between Nokia
+   mobile phones (e.g. Nokia 6110) and Nokia Cellular Data Suite software,
+   commercially sold by Nokia Corp.
+
+   The authentication scheme is based on the token send by the phone to the
+   software. The software does it's magic (see the function
+   FB61_GetNokiaAuth()) and returns the result back to the phone. If the
+   result is correct the phone responds with the message "Accessory
+   connected!" displayed on the LCD. Otherwise it will display "Accessory not
+   supported" and some functions will not be available for use.
+
+   The specification of the protocol is not publicly available, no comment. */
+void N6110_GetNokiaAuth(unsigned char *Imei, unsigned char *MagicBytes, unsigned char *MagicResponse)
+{
+
+  int i, j, CRC=0;
+
+  /* This is our temporary working area. */
+
+  unsigned char Temp[16];
+
+  /* Here we put FAC (Final Assembly Code) and serial number into our area. */
+
+  Temp[0]  = Imei[6];
+  Temp[1]  = Imei[7];
+  Temp[2]  = Imei[8];
+  Temp[3]  = Imei[9];
+  Temp[4]  = Imei[10];
+  Temp[5]  = Imei[11];
+  Temp[6]  = Imei[12];
+  Temp[7]  = Imei[13];
+
+  /* And now the TAC (Type Approval Code). */
+
+  Temp[8]  = Imei[2];
+  Temp[9]  = Imei[3];
+  Temp[10] = Imei[4];
+  Temp[11] = Imei[5];
+
+  /* And now we pack magic bytes from the phone. */
+
+  Temp[12] = MagicBytes[0];
+  Temp[13] = MagicBytes[1];
+  Temp[14] = MagicBytes[2];
+  Temp[15] = MagicBytes[3];
+
+  for (i=0; i<=11; i++)
+    if (Temp[i + 1]& 1)
+      Temp[i]<<=1;
+
+  switch (Temp[15] & 0x03) {
+
+  case 1:
+  case 2:
+    j = Temp[13] & 0x07;
+
+    for (i=0; i<=3; i++)
+      Temp[i+j] ^= Temp[i+12];
+
+    break;
+
+  default:
+    j = Temp[14] & 0x07;
+
+    for (i=0; i<=3; i++)
+      Temp[i + j] |= Temp[i + 12];
+  }
+
+  for (i=0; i<=15; i++)
+    CRC ^= Temp[i];
+
+  for (i=0; i<=15; i++) {
+
+    switch (Temp[15 - i] & 0x06) {
+
+    case 0:
+      j = Temp[i] | CRC;
+      break;
+
+    case 2:
+    case 4:
+      j = Temp[i] ^ CRC;
+      break;
+
+    case 6:
+      j = Temp[i] & CRC;
+      break;
+    }
+  
+    if (j == CRC)
+      j = 0x2c;
+
+    if (Temp[i] == 0)
+      j = 0;
+
+    MagicResponse[i] = j;
+
+  }
+}
+
+GSM_Error N6110_Authentication()
+{
+  unsigned char connect1[] = {N6110_FRAME_HEADER, 0x0d, 0x00, 0x00, 0x02};
+  unsigned char connect2[] = {N6110_FRAME_HEADER, 0x20, 0x02};
+  unsigned char connect3[] = {N6110_FRAME_HEADER, 0x0d, 0x01, 0x00, 0x02};
+  unsigned char connect4[] = {N6110_FRAME_HEADER, 0x10};
+  
+  unsigned char magic_connect[] = {N6110_FRAME_HEADER,
+  0x12,
+
+  /* The real magic goes here ... These bytes are filled in with the
+     function N6110_GetNokiaAuth(). */
+
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+
+  /* NOKIA&GNOKII Accessory */
+
+  0x4e, 0x4f, 0x4b, 0x49, 0x41, 0x26, 0x4e, 0x4f, 0x4b, 0x49, 0x41, 0x20,
+  0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x79,
+  
+  0x00, 0x00, 0x00, 0x00};
+
+#ifdef DEBUG
+  fprintf(stdout,_("Making authentication!\n"));
+#endif
+
+  usleep(100); Protocol->SendMessage(7, 0x02, connect1);
+  usleep(100); Protocol->SendMessage(5, 0x02, connect2);
+  usleep(100); Protocol->SendMessage(7, 0x02, connect3);
+      
+  CurrentMagicError = GE_BUSY;
+
+  usleep(100); Protocol->SendMessage(4, 0x64, connect4);
+  if (NULL_WaitUntil(50,&CurrentMagicError)!=GE_NONE) return GE_TIMEOUT;
+
+  N6110_GetNokiaAuth(Current_IMEI, MagicBytes, magic_connect+4);
+
+  Protocol->SendMessage(45, 0x64, magic_connect);
+
+#ifdef DEBUG
+  fprintf(stdout,_("End of authentication!\n"));
+#endif
+
+  return GE_NONE;
+}
+
+/* Initialise variables and state machine. */
+GSM_Error N6110_Initialise(char *port_device, char *initlength,
+                          GSM_ConnectionType connection,
+                          void (*rlp_callback)(RLP_F96Frame *frame))
+{
+  unsigned char init_char = N6110_SYNC_BYTE;
+  unsigned char end_init_char = N6110_IR_END_SYNC_BYTE;
+
+  int count;
+  int InitLength;
+  
+  if (Protocol->Initialise(port_device,initlength,connection,rlp_callback)!=GE_NONE)
+  {
+    return GE_NOTSUPPORTED;
+  }
+
+  switch (CurrentConnectionType) {
+    case GCT_Irda:
+    case GCT_MBUS:
+      /* We don't think about authentication in Irda, because
+         AFAIK there are no phones working over sockets
+        and having authentication. In MBUS it doesn't work */
+      usleep(100);
+
+      if (N6110_SendIDFrame()!=GE_NONE) return GE_TIMEOUT;
+    
+      if (N6110_SendIMEIFrame()!=GE_NONE) return GE_TIMEOUT;    
+
+      if (N6110_SendHWFrame()!=GE_NONE) return GE_TIMEOUT;    
+
+      CurrentLinkOK = true;                                 
+      break;
+      
+    case GCT_FBUS:
+    case GCT_Infrared:
+    case GCT_Tekram:
+      InitLength = atoi(initlength);
+
+      if ((strcmp(initlength, "default") == 0) || (InitLength == 0)) {
+        InitLength = 250;      /* This is the usual value, lower may work. */
+      }
+
+      if (CurrentConnectionType==GCT_Infrared ||
+          CurrentConnectionType==GCT_Tekram) {
+#ifdef DEBUG
+        fprintf(stdout,_("Setting infrared for FBUS communication...\n"));
+#endif
+        device_changespeed(9600);
+      }
+
+#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 ++) {
+        if (CurrentConnectionType!=GCT_Infrared &&
+            CurrentConnectionType!=GCT_Tekram)         usleep(100);
+        Protocol->WritePhone(1,&init_char);
+      }
+
+      if (CurrentConnectionType==GCT_Infrared ||
+          CurrentConnectionType==GCT_Tekram)      {
+        Protocol->WritePhone(1,&end_init_char);
+        usleep(200000);
+      }
+
+#ifdef DEBUG
+      fprintf(stdout,_("Done\n"));  
+#endif
+
+      if (CurrentConnectionType==GCT_Infrared ||
+          CurrentConnectionType==GCT_Tekram)      {
+        device_changespeed(115200);    
+      }
+
+      N6110_SendStatusRequest();
+    
+      usleep(100);
+
+      if (N6110_SendIDFrame()!=GE_NONE) return GE_TIMEOUT;
+    
+      /* N51xx/61xx have something called authentication.
+         After making it phone display "Accessory connected"
+         and probably give access to some function (I'm not too sure about it !)
+         Anyway, I make it now for N51xx/61xx */
+      if (GetModelFeature (FN_AUTHENTICATION)!=0) {
+        if (N6110_Authentication()!=GE_NONE) return GE_TIMEOUT;
+      } else {        /* No authentication */
+        if (N6110_SendIMEIFrame()!=GE_NONE) return GE_TIMEOUT;    
+
+        if (N6110_SendHWFrame()!=GE_NONE) return GE_TIMEOUT;    
+      }
+      
+      break;
+    default:
+#ifdef DEBUG
+      fprintf(stdout,_("Unknown connection type in n6110.c!\n"));
+#endif
+      break;
+  }
+
+  return (GE_NONE);
+}
+
+/* This function translates GMT_MemoryType to N6110_MEMORY_xx */
+int N6110_GetMemoryType(GSM_MemoryType memory_type)
+{
+
+  int result;
+
+  switch (memory_type) {
+
+     case GMT_MT: result = N6110_MEMORY_MT; break;
+     case GMT_ME: result = N6110_MEMORY_ME; break;
+     case GMT_SM: result = N6110_MEMORY_SM; break;
+     case GMT_FD: result = N6110_MEMORY_FD; break;
+     case GMT_ON: result = N6110_MEMORY_ON; break;
+     case GMT_EN: result = N6110_MEMORY_EN; break;
+     case GMT_DC: result = N6110_MEMORY_DC; break;
+     case GMT_RC: result = N6110_MEMORY_RC; break;
+     case GMT_MC: result = N6110_MEMORY_MC; break;
+     default    : result = N6110_MEMORY_XX;
+
+   }
+
+   return (result);
+}
+
+void N6110_ReplyCallDivert(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x02:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call divert status received\n"));
+    fprintf(stdout, _("   Divert type: "));
+    switch (MessageBuffer[6]) {
+      case 0x43: fprintf(stdout, _("when busy"));break;
+      case 0x3d: fprintf(stdout, _("when not answered"));break;
+      case 0x3e: fprintf(stdout, _("when phone off or no coverage"));break;
+      case 0x15: fprintf(stdout, _("all types of diverts"));break; //?
+      case 0x02: fprintf(stdout, _("all types of diverts"));break; //?
+      default:   fprintf(stdout, _("unknown %i"),MessageBuffer[6]);break;
+    }
+    fprintf(stdout, _("\n   Calls type : "));
+    if (MessageBuffer[6]==0x02)
+      fprintf(stdout, _("voice, fax & data")); //?
+    else {
+      switch (MessageBuffer[8]) {
+        case 0x0b: fprintf(stdout, _("voice"));break;
+        case 0x0d: fprintf(stdout, _("fax"));break;
+        case 0x19: fprintf(stdout, _("data"));break;
+        default:   fprintf(stdout, _("unknown %i"),MessageBuffer[8]);break;
+      }
+    }
+    fprintf(stdout, _("\n"));     
+    if (MessageBuffer[10]==0x01) {
+      fprintf(stdout, _("   Status     : active\n"));
+      fprintf(stdout, _("   Timeout    : %i seconds\n"),MessageBuffer[45]);
+      fprintf(stdout, _("   Number     : %s\n"),GSM_UnpackSemiOctetNumber(MessageBuffer+12,true));
+    } else {
+      fprintf(stdout, _("   Status     : deactivated\n"));     
+    }
+#endif /* DEBUG */
+
+    if (CurrentCallDivert!=NULL) { 
+      switch (MessageBuffer[6]) {
+        case 0x43: CurrentCallDivert->DType=GSM_CDV_Busy;break;
+        case 0x3d: CurrentCallDivert->DType=GSM_CDV_NoAnswer;break;
+        case 0x3e: CurrentCallDivert->DType=GSM_CDV_OutOfReach;break;
+        case 0x15: CurrentCallDivert->DType=GSM_CDV_AllTypes;break; //?
+        case 0x02: CurrentCallDivert->DType=GSM_CDV_AllTypes;break; //?
+      }
+
+      if (MessageBuffer[6]==0x02) //?
+        CurrentCallDivert->CType=GSM_CDV_AllCalls;
+      else {
+        switch (MessageBuffer[8]) {
+          case 0x0b: CurrentCallDivert->CType=GSM_CDV_VoiceCalls;break;
+          case 0x0d: CurrentCallDivert->CType=GSM_CDV_FaxCalls;  break;
+          case 0x19: CurrentCallDivert->CType=GSM_CDV_DataCalls; break;
+        }
+      }
+
+      if (MessageBuffer[10]==0x01) {
+        CurrentCallDivert->Enabled=true;
+        CurrentCallDivert->Timeout=MessageBuffer[45];
+        strcpy(CurrentCallDivert->Number,GSM_UnpackSemiOctetNumber(MessageBuffer+12,true));
+      } else {
+        CurrentCallDivert->Enabled=false;
+      }
+      CurrentCallDivertError=GE_NONE;
+    }
+    break;
+
+  case 0x03:
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call divert status receiving error ?\n"));
+#endif
+    CurrentCallDivertError=GE_UNKNOWN;
+    break;
+  }
+}
+
+GSM_Error N6110_CallDivert(GSM_CallDivert *cd)
+{
+  char req[55] = { N6110_FRAME_HEADER, 0x01,
+                                      0x00, /* operation */
+                                      0x00,
+                                      0x00, /* divert type */
+                                      0x00, /* call type */
+                                      0x00 };
+  GSM_Error error;
+
+  int length = 0x09;
+
+  switch (cd->Operation) {
+    case GSM_CDV_Register:
+    case GSM_CDV_Enable:
+      req[4] = 0x03;
+      req[8] = 0x01;
+      req[29]= GSM_PackSemiOctetNumber(cd->Number, req + 9, false);
+      req[52]= cd->Timeout;
+      length = 55;
+      break;
+    case GSM_CDV_Erasure:
+    case GSM_CDV_Disable:
+      req[4] = 0x04;
+      break;
+    case GSM_CDV_Query:
+      req[4] = 0x05;
+      break;
+    default:
+      return GE_NOTIMPLEMENTED;
+  }
+
+  switch (cd->DType) {
+    case GSM_CDV_AllTypes  : req[6] = 0x15; break;
+    case GSM_CDV_Busy      : req[6] = 0x43; break;
+    case GSM_CDV_NoAnswer  : req[6] = 0x3d; break;
+    case GSM_CDV_OutOfReach: req[6] = 0x3e; break;
+    default:                 return GE_NOTIMPLEMENTED;
+  }
+
+  if ((cd->DType == GSM_CDV_AllTypes) &&
+      (cd->CType == GSM_CDV_AllCalls))
+    req[6] = 0x02;
+
+  switch (cd->CType) {
+    case GSM_CDV_AllCalls  :                break;
+    case GSM_CDV_VoiceCalls: req[7] = 0x0b; break;
+    case GSM_CDV_FaxCalls  : req[7] = 0x0d; break;
+    case GSM_CDV_DataCalls : req[7] = 0x19; break;
+    default:                 return GE_NOTIMPLEMENTED;
+  }
+
+  CurrentCallDivert = cd;
+
+  error=NULL_SendMessageSequence
+    (100, &CurrentCallDivertError, length, 0x06, req);
+
+  CurrentCallDivert = NULL;
+
+  return error;
+}
+
+GSM_Error N6110_Tests()
+{
+  unsigned char buffer[3]={0x00,0x01,0xcf};
+  unsigned char buffer3[8]={0x00,0x01,0xce,0x1d,0xfe,0x23,0x00,0x00};
+  
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  //make almost all tests
+  Protocol->SendMessage(8, 0x40, buffer3);
+
+  while (GSM->Initialise(PortDevice, "50", CurrentConnectionType, CurrentRLP_RXCallback)!=GE_NONE) {};
+
+  sleep(2);
+
+  return NULL_SendMessageSequence
+    (200, &CurrentNetworkInfoError, 3, 0x40, buffer);  
+}
+
+void N6110_DisplayTestsInfo(u8 *MessageBuffer) {
+
+  int i;
+
+  CurrentNetworkInfoError=GE_NONE;
+
+  for (i=0;i<MessageBuffer[3];i++) {
+    switch (i) {
+      case 0: fprintf(stdout,_("Unknown(%i)              "),i);break;
+      case 1: fprintf(stdout,_("MCU ROM checksum        "));break;
+      case 2: fprintf(stdout,_("MCU RAM interface       "));break;
+      case 3: fprintf(stdout,_("MCU RAM component       "));break;
+      case 4: fprintf(stdout,_("MCU EEPROM interface    "));break;
+      case 5: fprintf(stdout,_("MCU EEPROM component    "));break;
+      case 6: fprintf(stdout,_("Real Time Clock battery "));break;
+      case 7: fprintf(stdout,_("CCONT interface         "));break;
+      case 8: fprintf(stdout,_("AD converter            "));break;
+      case 9: fprintf(stdout,_("SW Reset                "));break;
+      case 10:fprintf(stdout,_("Power Off               "));break;
+      case 11:fprintf(stdout,_("Security Data           "));break;
+      case 12:fprintf(stdout,_("EEPROM Tune checksum    "));break;
+      case 13:fprintf(stdout,_("PPM checksum            "));break;
+      case 14:fprintf(stdout,_("MCU download DSP        "));break;
+      case 15:fprintf(stdout,_("DSP alive               "));break;
+      case 16:fprintf(stdout,_("COBBA serial            "));break;
+      case 17:fprintf(stdout,_("COBBA paraller          "));break;
+      case 18:fprintf(stdout,_("EEPROM security checksum"));break;
+      case 19:fprintf(stdout,_("PPM validity            "));break;
+      case 20:fprintf(stdout,_("Warranty state          "));break;
+      case 21:fprintf(stdout,_("Simlock check           "));break;
+      case 22:fprintf(stdout,_("IMEI check?             "));break;//from PC-Locals.is OK?
+      default:fprintf(stdout,_("Unknown(%i)             "),i);break;
+    }
+    switch (MessageBuffer[4+i]) {
+      case 0:   fprintf(stdout,_(" : done, result OK"));break;
+      case 0xff:fprintf(stdout,_(" : not done, result unknown"));break;
+      case 254: fprintf(stdout,_(" : done, result NOT OK"));break;
+      default:  fprintf(stdout,_(" : result unknown(%i)"),MessageBuffer[4+i]);break;
+    }
+    fprintf(stdout,_("\n"));
+  }
+}
+
+void N6110_ReplySimlockInfo(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i, j;
+  
+  char uni[100];
+    
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Simlock info received\n"));
+
+  j=0;
+  for (i=0; i < 12; i++)
+  {
+    if (j<24) {
+      fprintf(stdout,_("%c"), ('0' + (MessageBuffer[9+i] >> 4)));
+      j++;
+    }
+    if (j==5 || j==15) fprintf(stdout, _("\n"));
+    if (j!=15) {
+      if (j<24) {
+        fprintf(stdout,_("%c"), ('0' + (MessageBuffer[9+i] & 0x0f)));
+       j++;
+      }
+    } else j++;
+    if (j==20 || j==24) fprintf(stdout, _("\n"));
+  }
+      
+  if ((MessageBuffer[6] & 1) == 1) fprintf(stdout,_("lock 1 closed\n"));
+  if ((MessageBuffer[6] & 2) == 2) fprintf(stdout,_("lock 2 closed\n"));
+  if ((MessageBuffer[6] & 4) == 4) fprintf(stdout,_("lock 3 closed\n"));
+  if ((MessageBuffer[6] & 8) == 8) fprintf(stdout,_("lock 4 closed\n"));
+
+  /* I'm not sure here at all */
+  if ((MessageBuffer[5] & 1) == 1) fprintf(stdout,_("lock 1 - user\n"));
+  if ((MessageBuffer[5] & 2) == 2) fprintf(stdout,_("lock 2 - user\n"));
+  if ((MessageBuffer[5] & 4) == 4) fprintf(stdout,_("lock 3 - user\n"));
+  if ((MessageBuffer[5] & 8) == 8) fprintf(stdout,_("lock 4 - user\n"));
+
+  fprintf(stdout,_("counter for lock1: %i\n"),MessageBuffer[21]);
+  fprintf(stdout,_("counter for lock2: %i\n"),MessageBuffer[22]);
+  fprintf(stdout,_("counter for lock3: %i\n"),MessageBuffer[23]);
+  fprintf(stdout,_("counter for lock4: %i\n"),MessageBuffer[24]);
+
+#endif
+
+  j=0;
+  for (i=0; i < 12; i++)
+  {
+    if (j<24) {
+      uni[j]='0' + (MessageBuffer[9+i] >> 4);
+      j++;
+    }
+    if (j!=15) {
+      if (j<24) {
+       uni[j]='0' + (MessageBuffer[9+i] & 0x0f);
+        j++;
+      }
+    } else j++;
+  }
+
+  strncpy(CurrentSimLock->simlocks[0].data,uni,5);
+  CurrentSimLock->simlocks[0].data[5]=0;
+  strncpy(CurrentSimLock->simlocks[3].data,uni+5,10);
+  CurrentSimLock->simlocks[3].data[10]=0;
+  strncpy(CurrentSimLock->simlocks[1].data,uni+16,4);
+  CurrentSimLock->simlocks[1].data[4]=0;
+  strncpy(CurrentSimLock->simlocks[2].data,uni+20,4);
+  CurrentSimLock->simlocks[2].data[4]=0;                                    
+
+  CurrentSimLock->simlocks[0].enabled=((MessageBuffer[6] & 1) == 1);
+  CurrentSimLock->simlocks[1].enabled=((MessageBuffer[6] & 2) == 2);
+  CurrentSimLock->simlocks[2].enabled=((MessageBuffer[6] & 4) == 4);
+  CurrentSimLock->simlocks[3].enabled=((MessageBuffer[6] & 8) == 8);
+
+  CurrentSimLock->simlocks[0].factory=((MessageBuffer[5] & 1) != 1);
+  CurrentSimLock->simlocks[1].factory=((MessageBuffer[5] & 2) != 2);
+  CurrentSimLock->simlocks[2].factory=((MessageBuffer[5] & 4) != 4);
+  CurrentSimLock->simlocks[3].factory=((MessageBuffer[5] & 8) != 8);
+
+  CurrentSimLock->simlocks[0].counter=MessageBuffer[21];
+  CurrentSimLock->simlocks[1].counter=MessageBuffer[22];
+  CurrentSimLock->simlocks[2].counter=MessageBuffer[23];
+  CurrentSimLock->simlocks[3].counter=MessageBuffer[24];
+
+  CurrentSimlockInfoError=GE_NONE;
+}
+
+GSM_Error N6110_SimlockInfo(GSM_AllSimlocks *siml)
+{
+  GSM_Error error;
+  unsigned char req[] = {0x00,0x01,0x8a,0x00};
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  CurrentSimLock=siml;
+  return NULL_SendMessageSequence (50, &CurrentSimlockInfoError, 4, 0x40, req);  
+}
+
+void N6110_ReplyResetPhoneSettings(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Resetting phone settings\n"));
+#endif /* DEBUG */
+
+  CurrentResetPhoneSettingsError=GE_NONE;
+}
+
+GSM_Error N6110_ResetPhoneSettings()
+{
+  GSM_Error error;
+  unsigned char req[] = {0x00,0x01,0x65,0x08,0x00};  
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentResetPhoneSettingsError, 5, 0x40, req);  
+}
+GSM_Error N6110_GetManufacturer(char *manufacturer)
+{
+       strcpy (manufacturer, "Nokia");
+       return (GE_NONE);
+}
+
+GSM_Error N6110_GetVoiceMailbox ( GSM_PhonebookEntry *entry)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x01, 0x00, 0x00, 0x00};
+
+  GSM_Error error;
+  
+  CurrentPhonebookEntry = entry;
+
+  req[4] = N6110_MEMORY_VOICE;
+  req[5] = 0x00; /* Location - isn't important, but... */
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentPhonebookError, 7, 0x03, req);
+    
+  CurrentPhonebookEntry = NULL;
+  
+  return error;
+}
+
+void N6110_ReplyGetOperatorName(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i, count;
+  
+  GSM_Bitmap NullBitmap;
+
+  DecodeNetworkCode(MessageBuffer+5, NullBitmap.netcode);
+  
+  count=8;
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Info about downloaded operator name received: %s network (for gnokii \"%s\", for phone \""),
+          NullBitmap.netcode,
+         GSM_GetNetworkName(NullBitmap.netcode));      
+#endif
+      
+  i=count;
+  while (MessageBuffer[count]!=0) {
+#ifdef DEBUG
+    fprintf(stdout,_("%c"),MessageBuffer[count]);
+#endif
+    count++;
+  }
+      
+ strcpy(CurrentGetOperatorNameNetwork->Code, NullBitmap.netcode);
+ strncpy(CurrentGetOperatorNameNetwork->Name, MessageBuffer+i,count-i+1);
+
+#ifdef DEBUG
+  fprintf(stdout,_("\")\n"));
+#endif
+          
+  CurrentGetOperatorNameError=GE_NONE;
+}
+
+GSM_Error N6110_GetOperatorName (GSM_Network *operator)
+{
+  unsigned char req[] = { 0x00,0x01,0x8c,0x00};
+
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  CurrentGetOperatorNameNetwork = operator;
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentGetOperatorNameError, 4, 0x40, req);
+
+  CurrentGetOperatorNameNetwork = NULL;
+  
+  return error;
+}
+
+void N6110_ReplySetOperatorName(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+    
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Downloaded operator name changed\n"));
+#endif    
+
+  CurrentSetOperatorNameError=GE_NONE;      
+}
+
+GSM_Error N6110_SetOperatorName (GSM_Network *operator)
+{
+  unsigned char req[256] = { 0x00,0x01,0x8b,0x00,
+                             0x00,0x00, /* MCC */
+                            0x00};     /* MNC */
+
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  EncodeNetworkCode(req+4,operator->Code);
+
+  strncpy(req+7,operator->Name,200);
+    
+  return NULL_SendMessageSequence
+    (20, &CurrentSetOperatorNameError, 8+strlen(operator->Name), 0x40, req);
+}
+
+void N6110_ReplyGetMemoryStatus(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x08:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Memory status received:\n"));
+
+    fprintf(stdout, _("   Memory Type: %s\n"), N6110_MemoryType_String[MessageBuffer[4]]);
+    fprintf(stdout, _("   Used: %d\n"), MessageBuffer[6]);
+    fprintf(stdout, _("   Free: %d\n"), MessageBuffer[5]);
+#endif /* DEBUG */
+
+    CurrentMemoryStatus->Used = MessageBuffer[6];
+    CurrentMemoryStatus->Free = MessageBuffer[5];
+    CurrentMemoryStatusError = GE_NONE;
+
+    break;
+
+  case 0x09:
+
+#ifdef DEBUG
+    switch (MessageBuffer[4]) {
+      case 0x6f:
+       fprintf(stdout, _("Message: Memory status error, phone is probably powered off.\n"));break;
+      case 0x7d:
+       fprintf(stdout, _("Message: Memory status error, memory type not supported by phone model.\n"));break;
+      case 0x8d:
+       fprintf(stdout, _("Message: Memory status error, waiting for security code.\n"));break;
+      default:
+       fprintf(stdout, _("Message: Unknown Memory status error, subtype (MessageBuffer[4]) = %02x\n"),MessageBuffer[4]);break;
+    }
+#endif
+
+    switch (MessageBuffer[4]) {
+      case 0x6f:CurrentMemoryStatusError = GE_TIMEOUT;break;
+      case 0x7d:CurrentMemoryStatusError = GE_INTERNALERROR;break;
+      case 0x8d:CurrentMemoryStatusError = GE_INVALIDSECURITYCODE;break;
+      default:break;
+    }
+
+    break;
+
+  }
+}
+
+/* This function is used to get storage status from the phone. It currently
+   supports two different memory areas - internal and SIM. */
+GSM_Error N6110_GetMemoryStatus(GSM_MemoryStatus *Status)
+{
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x07, /* MemoryStatus request */
+                          0x00  /* MemoryType */
+                        };
+
+  GSM_Error error;
+  
+  CurrentMemoryStatus = Status;
+
+  req[4] = N6110_GetMemoryType(Status->MemoryType);
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentMemoryStatusError, 5, 0x03, req);
+
+  CurrentMemoryStatus = NULL;
+
+  return error;
+}
+
+void N6110_ReplyGetNetworkInfo(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  GSM_NetworkInfo NullNetworkInfo;
+  
+  /* Make sure we are expecting NetworkInfo frame */
+  if (CurrentNetworkInfo && CurrentNetworkInfoError == GE_BUSY) {
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Network informations:\n"));
+#endif
+  } else {
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Network informations not requested, but received:\n"));
+#endif
+  }
+      
+  sprintf(NullNetworkInfo.NetworkCode, "%x%x%x %x%x", MessageBuffer[14] & 0x0f, MessageBuffer[14] >>4, MessageBuffer[15] & 0x0f, MessageBuffer[16] & 0x0f, MessageBuffer[16] >>4);
+
+  sprintf(NullNetworkInfo.CellID, "%02x%02x", MessageBuffer[10], MessageBuffer[11]);
+
+  sprintf(NullNetworkInfo.LAC, "%02x%02x", MessageBuffer[12], MessageBuffer[13]);
+
+#ifdef DEBUG
+  fprintf(stdout, _("   CellID: %s\n"), NullNetworkInfo.CellID);
+  fprintf(stdout, _("   LAC: %s\n"), NullNetworkInfo.LAC);
+  fprintf(stdout, _("   Network code: %s\n"), NullNetworkInfo.NetworkCode);
+  fprintf(stdout, _("   Network name: %s (%s)\n"),
+                     GSM_GetNetworkName(NullNetworkInfo.NetworkCode),
+                     GSM_GetCountryName(NullNetworkInfo.NetworkCode));
+  fprintf(stdout, _("   Status: "));
+
+  switch (MessageBuffer[8]) {
+    case 0x01: fprintf(stdout, _("home network selected")); break;
+    case 0x02: fprintf(stdout, _("roaming network")); break;
+    case 0x03: fprintf(stdout, _("requesting network")); break;
+    case 0x04: fprintf(stdout, _("not registered in the network")); break;
+    default: fprintf(stdout, _("unknown"));
+  }
+
+  fprintf(stdout, "\n");
+
+  fprintf(stdout, _("   Network selection: %s\n"), MessageBuffer[9]==1?_("manual"):_("automatic"));
+#endif /* DEBUG */
+
+  /* Make sure we are expecting NetworkInfo frame */
+  if (CurrentNetworkInfo && CurrentNetworkInfoError == GE_BUSY)
+    *CurrentNetworkInfo=NullNetworkInfo;
+
+  CurrentNetworkInfoError = GE_NONE;      
+}
+
+GSM_Error N6110_GetNetworkInfo(GSM_NetworkInfo *NetworkInfo)
+{
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x70
+                        };
+
+  GSM_Error error;
+  
+  CurrentNetworkInfo = NetworkInfo;
+  
+  error=NULL_SendMessageSequence
+    (20, &CurrentNetworkInfoError, 4, 0x0a, req);
+
+  CurrentNetworkInfo = NULL;
+
+  return error;
+}
+
+void N6110_ReplyGetProductProfileSetting(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i;
+  
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Product Profile Settings received -"));
+  for (i=0;i<4;i++) fprintf(stdout, _(" %02x"),MessageBuffer[3+i]);
+  fprintf(stdout, _("\n"));  
+#endif
+
+  for (i=0;i<4;i++) CurrentPPS[i]=MessageBuffer[3+i];
+
+  CurrentProductProfileSettingsError=GE_NONE;      
+}
+
+GSM_Error N6110_GetProductProfileSetting (GSM_PPS *PPS)
+{
+  unsigned char req[] = { 0x00, 0x01,0x6a };
+  
+  int i,j;
+
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentProductProfileSettingsError, 3, 0x40, req);
+  if (error!=GE_NONE) return error;    
+  
+  switch (PPS->Name) {
+    case PPS_ALS      : PPS->bool_value=(CurrentPPS[1]&32); break;
+    case PPS_GamesMenu: PPS->bool_value=(CurrentPPS[3]&64); break;
+    case PPS_HRData   : PPS->bool_value=(CurrentPPS[0]&64); break;
+    case PPS_14400Data: PPS->bool_value=(CurrentPPS[0]&128);break;
+    case PPS_EFR      : PPS->int_value =(CurrentPPS[0]&1)    +(CurrentPPS[0]&2);    break;
+    case PPS_FR       : PPS->int_value =(CurrentPPS[0]&16)/16+(CurrentPPS[0]&32)/16;break;
+    case PPS_HR       : PPS->int_value =(CurrentPPS[0]&4)/4  +(CurrentPPS[0]&8)/4;  break;
+    case PPS_VibraMenu: PPS->bool_value=(CurrentPPS[4]&64); break;
+    case PPS_LCDContrast:
+         PPS->int_value=0;
+         j=1;
+         for (i=0;i<5;i++) {
+          if (CurrentPPS[3]&j) PPS->int_value=PPS->int_value+j;
+          j=j*2;
+        }
+        PPS->int_value=PPS->int_value*100/32;
+         break;
+
+  }
+  
+  return (GE_NONE);
+}
+
+void N6110_ReplySetProductProfileSetting(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  int i;
+  
+  fprintf(stdout, _("Message: Product Profile Settings set to"));
+  for (i=0;i<4;i++) fprintf(stdout, _(" %02x"),CurrentPPS[i]);
+  fprintf(stdout, _("\n"));  
+#endif
+     
+  CurrentProductProfileSettingsError=GE_NONE;     
+}
+
+GSM_Error N6110_SetProductProfileSetting (GSM_PPS *PPS)
+{
+  unsigned char req[] = { 0x00, 0x01,0x6b, 
+                          0x00, 0x00, 0x00, 0x00 }; /* bytes with Product Profile Setings */
+  unsigned char settings[32];
+  
+  GSM_PPS OldPPS;
+  
+  int i,j,z;
+  
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+  
+  OldPPS.Name=PPS_ALS;
+  error=N6110_GetProductProfileSetting(&OldPPS);
+  if (error!=GE_NONE) return error;
+  
+  j=128;z=0;
+  for (i=0;i<32;i++) {
+    if (CurrentPPS[z]&j)
+      settings[i]='1';
+    else
+      settings[i]='0';    
+    if (j==1) {
+      j=128;
+      z++;
+    } else j=j/2;
+  }
+  
+#ifdef DEBUG
+  fprintf(stdout,_("Current settings: "));
+  for (i=0;i<32;i++) {
+    fprintf(stdout,_("%c"),settings[i]);    
+  }
+  fprintf(stdout,_("\n"));
+#endif
+
+  switch (PPS->Name) {
+    case PPS_ALS      :settings[10]=PPS->bool_value?'1':'0';break;
+    case PPS_HRData   :settings[ 5]=PPS->bool_value?'1':'0';break;
+    case PPS_14400Data:settings[ 6]=PPS->bool_value?'1':'0';break;
+    default           :break;
+  }
+    
+  j=128;z=0;
+  for (i=0;i<32;i++) {
+    if (settings[i]=='1') req[z+3]=req[z+3]+j;
+    if (j==1) {
+      j=128;
+      z++;
+    } else j=j/2;
+  }  
+
+#ifdef DEBUG
+  fprintf(stdout,_("Current settings: "));
+  for (i=0;i<4;i++) {
+    fprintf(stdout,_("%i "),req[i+3]);    
+  }
+  fprintf(stdout,_("\n"));
+#endif
+
+  for (i=0;i<4;i++) {
+   CurrentPPS[i]=req[i+3];    
+  }
+
+  return NULL_SendMessageSequence
+    (20, &CurrentProductProfileSettingsError, 7, 0x40, req);
+}
+
+void N6110_ReplyPressKey(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+    if (MessageBuffer[4]==CurrentPressKeyEvent) CurrentPressKeyError=GE_NONE;
+                                           else CurrentPressKeyError=GE_UNKNOWN; /* MessageBuffer[4] = 0x05 */
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Result of key "));
+    switch (MessageBuffer[4])
+    {
+      case PRESSPHONEKEY:   fprintf(stdout, _("press OK\n"));break;
+      case RELEASEPHONEKEY: fprintf(stdout, _("release OK\n"));break;
+      default:              fprintf(stdout, _("press or release - error\n"));break;
+    }
+#endif /* DEBUG */
+}
+
+GSM_Error N6110_PressKey(int key, int event)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x42, 0x01, 0x00, 0x01};
+  
+  req[4]=event; /* if we press or release key */
+  req[5]=key;
+  
+  CurrentPressKeyEvent=event;
+  
+  return NULL_SendMessageSequence
+    (10, &CurrentPressKeyError, 7, 0x0c, req);
+}
+
+void N6110_ReplyDisplayOutput(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  /* Hopefully is 64 larger as FB38_MAX* / N6110_MAX* */
+  char model[64];
+
+  int i, j;
+
+  char uni[100];
+    
+  switch(MessageBuffer[3]) {
+
+  /* Phone sends displayed texts */
+  case 0x50:
+    NewX=MessageBuffer[6];
+    NewY=MessageBuffer[5];
+
+    DecodeUnicode (uni, MessageBuffer+8, MessageBuffer[7]);
+
+#ifdef DEBUG
+    fprintf(stdout, _("New displayed text (%i %i): \"%s\"\n"),NewX,NewY,uni);      
+#endif /* DEBUG */
+
+    while (N6110_GetModel(model)  != GE_NONE)
+      sleep(1);
+
+    /* With these rules it works almost excellent with my N5110 */
+    /* I don't have general rule :-(, that's why you must experiment */
+    /* with your phone. Nokia could make it better. MW */
+    /* It's almost OK for N5110*/
+    /* FIX ME: it will be the same for N5130 and 3210 too*/
+    if (!strcmp(model,"NSE-1"))
+    {
+      /* OldX==1000 means - it's first time */
+      if (OldX==1000) {
+      
+        /* Clean table */
+        for (i=0;i<5+1;i++) {
+          for (j=0;j<27+1;j++) {PhoneScreen[i][j]=' ';}
+      }
+      OldX=0;
+    }
+
+    if ((OldX==0 && OldY==31 && NewX==29 && NewY==46) ||
+        (OldX==0 && OldY==13 && NewX==23 && NewY==46)) {
+      /* Clean the line with current text */
+      for (j=0;j<27+1;j++) {PhoneScreen[NewY/(47/5)][j]=' ';}
+      
+      /* Inserts text into table */
+      for (i=0; i<MessageBuffer[7];i++) {      
+        PhoneScreen[NewY/(47/5)][NewX/(84/27)+i]=uni[i];
+      }
+
+    }
+
+    if ((OldX==0 && OldY==21 && NewX==0 && NewY==10) ||
+        (OldX==0 && OldY==10 && NewX==35 && NewY==46)) {
+    } else {
+      if ((OldX!=0 && NewX==0 && NewY!=6) ||
+          (OldX==0 && NewX!=0 && OldY!=13 && OldY!=22) ||
+          (OldX==0 && NewX==0 && NewY<OldY && (NewY!=13 || OldY!=26)) ||
+          (OldY==5 && NewY!=5) ||
+          (OldX==0 && OldY==13 && NewX==23 && NewY==46)) {
+
+        /* Writes "old" screen */
+        for (i=0;i<5+1;i++) {
+          for (j=0;j<27+1;j++) {fprintf(stdout,_("%c"),PhoneScreen[i][j]);}
+           fprintf(stdout,_("\n"));
+         }
+       
+         /* Clean table */
+          for (i=0;i<5+1;i++) {
+           for (j=0;j<27+1;j++) {PhoneScreen[i][j]=' ';}
+         }
+        }
+      }
+      
+      /* Clean the line with current text */
+      for (j=0;j<27+1;j++) {PhoneScreen[NewY/(47/5)][j]=' ';}
+      
+      /* Inserts text into table */
+      for (i=0; i<MessageBuffer[7];i++) {      
+        PhoneScreen[NewY/(47/5)][NewX/(84/27)+i]=uni[i];
+      }
+      
+      OldY=NewY;
+      OldX=NewX;
+    } else {
+#ifndef DEBUG
+      fprintf(stdout, _("%s\n"),uni);      
+#endif
+    }
+
+    break;
+  case 0x54:
+      
+    if (MessageBuffer[4]==1)
+    {
+      
+#ifdef DEBUG
+      fprintf(stdout, _("Display output successfully disabled/enabled.\n"));
+#endif /* DEBUG */
+
+      CurrentDisplayOutputError=GE_NONE;
+    }
+       
+    break;
+  }
+}
+
+GSM_Error SetDisplayOutput(unsigned char state)
+{
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x53, 0x00};
+
+  req[4]=state;
+  
+  return NULL_SendMessageSequence
+    (30, &CurrentDisplayOutputError, 5, 0x0d, req);
+}
+
+GSM_Error N6110_EnableDisplayOutput()
+{
+  return SetDisplayOutput(0x01);
+}
+GSM_Error N6110_DisableDisplayOutput()
+{
+  return SetDisplayOutput(0x02);
+}
+
+/* If it is interesting for somebody: we can use 0x40 msg for it
+   and it will work for all phones. See n6110.txt for details */
+GSM_Error N6110_AnswerCall(char s)
+{
+       unsigned char req0[] = { N6110_FRAME_HEADER, 0x42,0x05,0x01,0x07,                                0xa2,0x88,0x81,0x21,0x15,0x63,0xa8,0x00,0x00,
+                           0x07,0xa3,0xb8,0x81,0x20,0x15,0x63,0x80};
+       unsigned char req[] = { N6110_FRAME_HEADER, 0x06, 0x00, 0x00};
+
+       req[4]=s;
+
+#ifdef DEBUG
+       fprintf(stdout,_("Answering call %d\n\r"),s);
+#endif
+
+       Protocol->SendMessage(sizeof(req0), 0x01, req0);
+       sleep(1);
+
+       return NULL_SendMessageSequence
+               (20, &CurrentMagicError, sizeof(req) , 0x01, req);
+}
+
+void N6110_ReplyGetProfile(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  /* Profile feature */
+  case 0x14:   
+
+    switch(GetModelFeature (FN_PROFILES)) {
+      case F_PROF33:
+        switch (MessageBuffer[6]) {
+          case 0x00: CurrentProfile->KeypadTone  = MessageBuffer[8]; break;
+          case 0x01: CurrentProfile->CallAlert   = MessageBuffer[8]; break;
+          case 0x02: CurrentProfile->Ringtone    = MessageBuffer[8]; break;
+          case 0x03: CurrentProfile->Volume      = MessageBuffer[8]; break;
+          case 0x04: CurrentProfile->MessageTone = MessageBuffer[8]; break;
+          case 0x05: CurrentProfile->Vibration   = MessageBuffer[8]; break;
+          case 0x06: CurrentProfile->WarningTone = MessageBuffer[8]; break;
+          case 0x07: CurrentProfile->ScreenSaver = MessageBuffer[8]; break;      
+          default:
+#ifdef DEBUG
+           fprintf(stdout,_("feature %i = value %i\n\n"),MessageBuffer[6],MessageBuffer[8]);
+#endif
+            break;
+        }
+        break;
+      default:
+        switch (MessageBuffer[6]) {
+          case 0x00: CurrentProfile->KeypadTone      = MessageBuffer[8];break;
+          case 0x01: CurrentProfile->Lights          = MessageBuffer[8];break;
+          case 0x02: CurrentProfile->CallAlert       = MessageBuffer[8];break;
+          case 0x03: CurrentProfile->Ringtone        = MessageBuffer[8];break;
+          case 0x04: CurrentProfile->Volume          = MessageBuffer[8];break;
+          case 0x05: CurrentProfile->MessageTone     = MessageBuffer[8];break;
+          case 0x06: CurrentProfile->Vibration       = MessageBuffer[8];break;
+          case 0x07: CurrentProfile->WarningTone     = MessageBuffer[8];break;
+          case 0x08: CurrentProfile->CallerGroups    = MessageBuffer[8];break;
+          case 0x09: CurrentProfile->AutomaticAnswer = MessageBuffer[8];break;
+          default:
+#ifdef DEBUG
+           fprintf(stdout,_("feature %i = value %i\n\n"),MessageBuffer[6],MessageBuffer[8]);
+#endif
+            break;
+        }
+        break;
+    }
+
+    CurrentProfileError = GE_NONE;
+    break;
+
+  /* Incoming profile name */
+  case 0x1b:   
+
+    if (MessageBuffer[9] == 0x00) {
+      CurrentProfile->DefaultName=MessageBuffer[8];
+    } else {
+      CurrentProfile->DefaultName=-1;      
+       
+      /* Here name is in Unicode */
+      if (GetModelFeature (FN_PROFILES)==F_PROF33) {
+       DecodeUnicode (CurrentProfile->Name, MessageBuffer+10, MessageBuffer[9]/2);
+      } else {
+        /* ...here not */
+        sprintf(CurrentProfile->Name, MessageBuffer + 10, MessageBuffer[9]);
+        CurrentProfile->Name[MessageBuffer[9]] = '\0';
+      }
+    }
+
+    CurrentProfileError = GE_NONE;
+    break;
+
+  }
+}
+
+/* Needs SIM card with PIN in phone */
+GSM_Error N6110_GetProfile(GSM_Profile *Profile)
+{
+  int i;
+  
+  unsigned char name_req[] = { N6110_FRAME_HEADER, 0x1a, 0x00};
+  unsigned char feat_req[] = { N6110_FRAME_HEADER, 0x13, 0x01, 0x00, 0x00};
+
+  GSM_Error error;
+  
+  CurrentProfile = Profile;
+
+  /* When after sending all frames feature==253, it means, that it is not
+     supported */
+  CurrentProfile->KeypadTone=253;
+  CurrentProfile->Lights=253;    
+  CurrentProfile->CallAlert=253; 
+  CurrentProfile->Ringtone=253;  
+  CurrentProfile->Volume=253;    
+  CurrentProfile->MessageTone=253;
+  CurrentProfile->WarningTone=253;
+  CurrentProfile->Vibration=253;  
+  CurrentProfile->CallerGroups=253;
+  CurrentProfile->ScreenSaver=253; 
+  CurrentProfile->AutomaticAnswer=253;
+
+  name_req[4] = Profile->Number;
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentProfileError, 5, 0x05, name_req);
+  if (error!=GE_NONE) return error;
+
+  for (i = 0x00; i <= 0x09; i++) {
+
+    feat_req[5] = Profile->Number;
+    
+    feat_req[6] = i;
+
+    error=NULL_SendMessageSequence
+      (20, &CurrentProfileError, 7, 0x05, feat_req);
+    if (error!=GE_NONE) return error;
+  }
+
+  if (Profile->DefaultName > -1)
+  {
+    switch(GetModelFeature (FN_PROFILES)) {
+      case F_PROF33:
+        switch (Profile->DefaultName) {
+          case 0x00: sprintf(Profile->Name, "General");break;
+          case 0x01: sprintf(Profile->Name, "Silent");break;
+          case 0x02: sprintf(Profile->Name, "Descreet");break;
+          case 0x03: sprintf(Profile->Name, "Loud");break;
+          case 0x04: sprintf(Profile->Name, "My style");break;
+          case 0x05: Profile->Name[0]=0;break;
+          default  : sprintf(Profile->Name, "Unknown (%i)", Profile->DefaultName);break;
+       }
+        break;
+      case F_PROF51:
+        switch (Profile->DefaultName) {
+          case 0x00: sprintf(Profile->Name, "Personal");break;
+          case 0x01: sprintf(Profile->Name, "Car");break;
+          case 0x02: sprintf(Profile->Name, "Headset");break;
+          default  : sprintf(Profile->Name, "Unknown (%i)", Profile->DefaultName);break;
+        }
+        break;
+      case F_PROF61:
+        switch (Profile->DefaultName) {
+          case 0x00: sprintf(Profile->Name, "General");break;
+          case 0x01: sprintf(Profile->Name, "Silent");break;
+          case 0x02: sprintf(Profile->Name, "Meeting");break;
+          case 0x03: sprintf(Profile->Name, "Outdoor");break;
+          case 0x04: sprintf(Profile->Name, "Pager");break;
+          case 0x05: sprintf(Profile->Name, "Car");break;
+          case 0x06: sprintf(Profile->Name, "Headset");break;
+          default  : sprintf(Profile->Name, "Unknown (%i)", Profile->DefaultName);break;
+       }
+        break;
+    }
+  }
+  
+  return (GE_NONE);
+
+}
+
+void N6110_ReplySetProfile(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  /* Profile feature change result */
+  case 0x11:   
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Profile feature change result.\n"));
+#endif /* DEBUG */
+    CurrentProfileError = GE_NONE;
+    break;
+
+  /* Profile name set result */
+  case 0x1d:   
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Profile name change result.\n"));
+#endif /* DEBUG */
+    CurrentProfileError = GE_NONE;
+    break;
+
+  }
+}
+
+GSM_Error N6110_SetProfileFeature(u8 profile, u8 feature, u8 value)
+{
+  unsigned char feat_req[] = { N6110_FRAME_HEADER, 0x10, 0x01,
+                               0x00, 0x00, 0x00};
+
+  feat_req[5]=profile;
+  feat_req[6]=feature;
+  feat_req[7]=value;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentProfileError, 8, 0x05, feat_req);
+}
+
+GSM_Error N6110_SetProfile(GSM_Profile *Profile)
+{
+  int i,value;
+
+  unsigned char name_req[40] = { N6110_FRAME_HEADER, 0x1c, 0x01, 0x03,
+                                 0x00, 0x00, 0x00};
+
+  GSM_Error error;
+  
+  name_req[7] = Profile->Number;
+  name_req[8] = strlen(Profile->Name);
+  name_req[6] = name_req[8] + 2;
+
+  for (i = 0; i < name_req[8]; i++)
+    name_req[9 + i] = Profile->Name[i];
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentProfileError, name_req[8] + 9, 0x05, name_req);
+  if (error!=GE_NONE) return error;
+
+  for (i = 0x00; i <= 0x09; i++) {
+
+    switch (i) {
+      case 0x00: value = Profile->KeypadTone; break;
+      case 0x01: value = Profile->Lights; break;
+      case 0x02: value = Profile->CallAlert; break;
+      case 0x03: value = Profile->Ringtone; break;
+      case 0x04: value = Profile->Volume; break;
+      case 0x05: value = Profile->MessageTone; break;
+      case 0x06: value = Profile->Vibration; break;
+      case 0x07: value = Profile->WarningTone; break;
+      case 0x08: value = Profile->CallerGroups; break;
+      case 0x09: value = Profile->AutomaticAnswer; break;
+      default  : value = 0; break;
+    }
+
+    error=N6110_SetProfileFeature(Profile->Number,i,value);
+    if (error!=GE_NONE) return error;
+  }
+
+  return (GE_NONE);
+}
+
+bool N6110_SendRLPFrame(RLP_F96Frame *frame, bool out_dtx)
+{
+  u8 req[60] = { 0x00, 0xd9 };
+               
+  /* Discontinuos transmission (DTX).  See section 5.6 of GSM 04.22 version
+     7.0.1. */
+       
+  if (out_dtx)
+    req[1]=0x01;
+
+  memcpy(req+2, (u8 *) frame, 32);
+
+  return (Protocol->SendFrame(32, 0xf0, req));
+}
+
+void N6110_ReplyGetCalendarNote(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i, j;
+
+  u8 mychar1;
+
+  wchar_t wc;  
+    
+  switch (MessageBuffer[4]) {
+
+    case 0x01:
+      
+      CurrentCalendarNote->Type=MessageBuffer[8];
+
+      DecodeDateTime(MessageBuffer+9, &CurrentCalendarNote->Time);
+
+      DecodeDateTime(MessageBuffer+16, &CurrentCalendarNote->Alarm);
+
+      CurrentCalendarNote->Text[0]=0;
+      
+      if (GetModelFeature (FN_CALENDAR)==F_CAL33) {
+        i=0;
+        if (CurrentCalendarNote->Type == GCN_REMINDER) i=1; //first char is subset
+        switch (MessageBuffer[24]) {
+          case 3:
+#ifdef DEBUG
+            fprintf(stdout,_("Subset 3 in reminder note !\n"));
+#endif
+            while (i!=MessageBuffer[23]) {
+              j=0;
+              if (i!=MessageBuffer[23]-1) {
+               if (MessageBuffer[24+i]>=0xc2) {
+                 DecodeWithUTF8Alphabet(MessageBuffer[24+i], MessageBuffer[24+i+1], &mychar1);
+                  CurrentCalendarNote->Text[strlen(CurrentCalendarNote->Text)+1]=0;
+                  CurrentCalendarNote->Text[strlen(CurrentCalendarNote->Text)]=mychar1;
+                 j=-1;
+                 i++;
+               }
+             }
+              if (j!=-1) {
+                CurrentCalendarNote->Text[strlen(CurrentCalendarNote->Text)+1]=0;
+                CurrentCalendarNote->Text[strlen(CurrentCalendarNote->Text)]=MessageBuffer[24+i];
+              }
+             i++;
+            }
+            break;
+          case 2:
+#ifdef DEBUG
+            fprintf(stdout,_("Subset 2 in reminder note !\n"));
+#endif
+            while (i!=MessageBuffer[23]) {
+             wc = MessageBuffer[24+i] | (0x00 << 8);
+              CurrentCalendarNote->Text[strlen(CurrentCalendarNote->Text)+1]=0;
+              CurrentCalendarNote->Text[strlen(CurrentCalendarNote->Text)]=
+                     DecodeWithUnicodeAlphabet(wc);
+             i++;
+            }
+            break;
+          case 1:
+#ifdef DEBUG
+            fprintf(stdout,_("Subset 1 in reminder note !\n"));
+#endif
+            memcpy(CurrentCalendarNote->Text,MessageBuffer+24+i,MessageBuffer[23]-i);
+            CurrentCalendarNote->Text[MessageBuffer[23]-i]=0;
+            break;
+          default:
+#ifdef DEBUG
+            fprintf(stdout,_("Unknown subset in reminder note !\n"));
+#endif
+            memcpy(CurrentCalendarNote->Text,MessageBuffer+24+i,MessageBuffer[23]-i);
+            CurrentCalendarNote->Text[MessageBuffer[23]-i]=0;
+            break;
+        }
+      } else {
+        memcpy(CurrentCalendarNote->Text,MessageBuffer+24,MessageBuffer[23]);
+        CurrentCalendarNote->Text[MessageBuffer[23]]=0;
+      }
+      
+      if (CurrentCalendarNote->Type == GCN_CALL) {
+        memcpy(CurrentCalendarNote->Phone,MessageBuffer+24+MessageBuffer[23]+1,MessageBuffer[24+MessageBuffer[23]]);
+        CurrentCalendarNote->Phone[MessageBuffer[24+MessageBuffer[23]]]=0;
+      }
+
+      CurrentCalendarNote->Recurrance=0;
+
+      CurrentCalendarNote->AlarmType=0;
+
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Calendar note received.\n"));
+
+      fprintf(stdout, _("   Date: %d-%02d-%02d\n"), CurrentCalendarNote->Time.Year,
+                                           CurrentCalendarNote->Time.Month,
+                                           CurrentCalendarNote->Time.Day);
+
+      fprintf(stdout, _("   Time: %02d:%02d:%02d\n"), CurrentCalendarNote->Time.Hour,
+                                           CurrentCalendarNote->Time.Minute,
+                                           CurrentCalendarNote->Time.Second);
+
+      /* Some messages do not have alarm set up */
+      if (CurrentCalendarNote->Alarm.Year != 0) {
+        fprintf(stdout, _("   Alarm date: %d-%02d-%02d\n"), CurrentCalendarNote->Alarm.Year,
+                                                CurrentCalendarNote->Alarm.Month,
+                                                CurrentCalendarNote->Alarm.Day);
+
+       fprintf(stdout, _("   Alarm time: %02d:%02d:%02d\n"), CurrentCalendarNote->Alarm.Hour,
+                                                CurrentCalendarNote->Alarm.Minute,
+                                                 CurrentCalendarNote->Alarm.Second);
+      }
+
+      fprintf(stdout, _("   Type: %d\n"), CurrentCalendarNote->Type);
+      fprintf(stdout, _("   Text: %s\n"), CurrentCalendarNote->Text);
+
+      if (CurrentCalendarNote->Type == GCN_CALL)
+        fprintf(stdout, _("   Phone: %s\n"), CurrentCalendarNote->Phone);
+#endif /* DEBUG */
+
+      CurrentCalendarNoteError=GE_NONE;
+      break;
+
+    case 0x93:
+
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Calendar note not available\n"));
+#endif /* DEBUG */
+
+      CurrentCalendarNoteError=GE_INVALIDCALNOTELOCATION;
+      break;
+
+    default:
+
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Calendar note error\n"));
+#endif /* DEBUG */
+
+      CurrentCalendarNoteError=GE_INTERNALERROR;
+      break;
+
+  }
+}
+
+GSM_Error N6110_GetCalendarNote(GSM_CalendarNote *CalendarNote)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x66, 0x00
+                        };
+  GSM_Error error;
+
+  req[4]=CalendarNote->Location;
+
+  CurrentCalendarNote = CalendarNote;
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentCalendarNoteError, 5, 0x13, req);
+
+  CurrentCalendarNote = NULL;
+
+  return error;
+}
+
+void N6110_ReplyWriteCalendarNote(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  switch(MessageBuffer[4]) {
+    /* This message is also sent when the user enters the new entry on keypad */
+    case 0x01:
+      fprintf(stdout, _("Message: Calendar note write succesfull!\n"));break;      
+    case 0x73:
+      fprintf(stdout, _("Message: Calendar note write failed!\n"));break;
+    case 0x7d:
+      fprintf(stdout, _("Message: Calendar note write failed!\n"));break;
+    default:
+      fprintf(stdout, _("Unknown message of type 0x13 and subtype 0x65\n"));break;
+  }
+#endif
+
+  switch(MessageBuffer[4]) {
+    case 0x01: CurrentCalendarNoteError=GE_NONE; break;      
+    case 0x73: CurrentCalendarNoteError=GE_INTERNALERROR; break;
+    case 0x7d: CurrentCalendarNoteError=GE_INTERNALERROR; break;
+    default  : AppendLogText("Unknown msg\n",false); break;
+  }
+}
+
+GSM_Error N6110_WriteCalendarNote(GSM_CalendarNote *CalendarNote)
+{
+
+  unsigned char req[200] = { N6110_FRAME_HEADER,
+                             0x64, 0x01, 0x10,
+                             0x00, /* Length of the rest of the frame. */
+                             0x00, /* The type of calendar note */
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+                        };
+
+  typedef struct {
+    char *model;
+    unsigned char call;
+    unsigned char meeting;
+    unsigned char birthday;
+    unsigned char reminder;
+  } calendar_model_length;
+  
+  /* Length of entries */
+  calendar_model_length calendar_lengths[] =
+  {
+    /*model,CallTo,Meeting,Birthday,Reminder*/
+    {"NHM-5",0x24,0x24,0x24,0x24}, //Reminder from phone, other quesses
+    {"NHM-6",0x24,0x24,0x24,0x24}, //Reminder from phone, other quesses
+    {"NSE-3",0x1e,0x14,0x14,0x1e}, //from NCDS3 [HKEY_LOCAL_MACHINE\Software\Nokia\Data Suite\3.0\Calendar]
+    {"NSM-1",0x1e,0x18,0x18,0x24}, //from NCDS3 
+    {"NSK-3",0x1e,0x14,0x14,0x1e}, //from NCDS3 
+    {"NSB-3",0x20,0x14,0x14,0x1e}, //from NCDS3
+    {"",     0,   0,   0,   0   }  //end of table
+  };
+
+  int i, j, current;
+
+  u8 mychar;
+  
+  u8 mychar1,mychar2;
+  
+  GSM_Error error;
+  
+  /* Hopefully is 64 larger as FB38_MAX* / N6110_MAX* */
+  char model[64];
+
+  req[7]=CalendarNote->Type;
+
+  EncodeDateTime(req+8, &CalendarNote->Time);
+  req[14] = CalendarNote->Time.Timezone;
+
+  if (CalendarNote->Alarm.Year) {
+    EncodeDateTime(req+15, &CalendarNote->Alarm);
+    req[21] = CalendarNote->Alarm.Timezone;
+  }
+
+  req[22]=strlen(CalendarNote->Text);
+  
+  current=23;
+
+  if (GetModelFeature (FN_CALENDAR)==F_CAL33 && CalendarNote->Type==GCN_REMINDER) {
+    req[22]++;           // one additional char
+    req[current++]=0x01; //we use now subset 1
+  }
+    
+  for (i=0; i<strlen(CalendarNote->Text); i++) {
+    j=0;
+    mychar=CalendarNote->Text[i];
+    if (GetModelFeature (FN_CALENDAR)==F_CAL33 && CalendarNote->Type==GCN_REMINDER) {
+      if (EncodeWithUTF8Alphabet(mychar,&mychar1,&mychar2)) {
+          req[current++]=mychar1;
+         req[current++]=mychar2;
+          req[23]=0x03; //use subset 3
+          req[22]++;    // one additional char
+         j=-1;
+      }
+    }
+    if (j!=-1) {
+      /* Enables/disables blinking */
+      if (mychar=='~') req[current++]=0x01;
+                  else req[current++]=mychar;
+    }
+  }
+
+  req[current++]=strlen(CalendarNote->Phone);
+
+  for (i=0; i<strlen(CalendarNote->Phone); i++)
+    req[current++]=CalendarNote->Phone[i];
+
+  while (N6110_GetModel(model)  != GE_NONE)
+    sleep(1);
+
+  /* Checking maximal length */
+  i=0;
+  while (strcmp(calendar_lengths[i].model,"")) {
+    if (!strcmp(calendar_lengths[i].model,model)) {
+      switch (CalendarNote->Type) {
+        case GCN_REMINDER:if (req[22]>calendar_lengths[i].reminder) return GE_TOOLONG;break;
+        case GCN_MEETING :if (req[22]>calendar_lengths[i].meeting)  return GE_TOOLONG;break;
+        case GCN_BIRTHDAY:if (req[22]>calendar_lengths[i].birthday) return GE_TOOLONG;break;
+        case GCN_CALL    :if (strlen(CalendarNote->Phone)>calendar_lengths[i].call) return GE_TOOLONG;break;
+      }
+      break;
+    }
+    i++;
+  }
+
+  CurrentCalendarNote = CalendarNote;
+
+  error=NULL_SendMessageSequence
+    (20, &CurrentCalendarNoteError, current, 0x13, req);
+
+  CurrentCalendarNote = NULL;
+
+  return error;
+}
+
+void N6110_ReplyDeleteCalendarNote(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  switch (MessageBuffer[4]) {
+    /* This message is also sent when the user deletes an old entry on
+       keypad or moves an old entry somewhere (there is also `write'
+       message). */
+    case 0x01:fprintf(stdout, _("Message: Calendar note deleted\n"));break;
+    case 0x93:fprintf(stdout, _("Message: Calendar note can't be deleted\n"));break;
+    default  :fprintf(stdout, _("Message: Calendar note deleting error\n"));break;
+  }
+#endif
+
+  switch (MessageBuffer[4]) {
+    case 0x01:CurrentCalendarNoteError=GE_NONE;break;
+    case 0x93:CurrentCalendarNoteError=GE_INVALIDCALNOTELOCATION;break;
+    default  :CurrentCalendarNoteError=GE_INTERNALERROR;break;
+  }
+}
+
+GSM_Error N6110_DeleteCalendarNote(GSM_CalendarNote *CalendarNote)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x68, 0x00
+                        };
+
+  req[4]=CalendarNote->Location;
+
+  return NULL_SendMessageSequence (20, &CurrentCalendarNoteError, 5, 0x13, req);
+}
+
+void N6110_ReplyRFBatteryLevel(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Phone status received:\n"));
+    fprintf(stdout, _("   Mode: "));
+
+    switch (MessageBuffer[4]) {
+
+      case 0x01:
+
+       fprintf(stdout, _("registered within the network\n"));
+       break;
+             
+      /* I was really amazing why is there a hole in the type of 0x02, now I
+        know... */
+      case 0x02: fprintf(stdout, _("call in progress\n"));          break; /* ringing or already answered call */
+      case 0x03: fprintf(stdout, _("waiting for security code\n")); break;
+      case 0x04: fprintf(stdout, _("powered off\n"));               break;
+      default  : fprintf(stdout, _("unknown\n"));
+
+    }
+
+    fprintf(stdout, _("   Power source: "));
+
+    switch (MessageBuffer[7]) {
+
+      case 0x01: fprintf(stdout, _("AC/DC\n"));   break;
+      case 0x02: fprintf(stdout, _("battery\n")); break;
+      default  : fprintf(stdout, _("unknown\n"));
+
+    }
+
+    fprintf(stdout, _("   Battery Level: %d\n"), MessageBuffer[8]);
+    fprintf(stdout, _("   Signal strength: %d\n"), MessageBuffer[5]);
+#endif /* DEBUG */
+
+    CurrentRFLevel=MessageBuffer[5];
+    CurrentBatteryLevel=MessageBuffer[8];
+    CurrentPowerSource=MessageBuffer[7];
+}
+
+
+GSM_Error N6110_GetRFLevel(GSM_RFUnits *units, float *level)
+{
+
+  /* FIXME - these values are from 3810 code, may be incorrect.  Map from
+     values returned in status packet to the the values returned by the AT+CSQ
+     command. */
+  float        csq_map[5] = {0, 8, 16, 24, 31};
+
+  int timeout=10;
+  int rf_level;
+  
+  char screen[NM_MAX_SCREEN_WIDTH];
+
+  CurrentRFLevel=-1;
+    
+  if (GetModelFeature (FN_NOPOWERFRAME)==F_NOPOWER) {
+
+    if (N6110_NetMonitor(1, screen)!=GE_NONE)
+      return GE_INTERNALERROR;
+    
+    rf_level=4;
+    
+    if (screen[4]!='-') {
+      if (screen[5]=='9' && screen[6]>'4') rf_level=1;
+      if (screen[5]=='9' && screen[6]<'5') rf_level=2;
+      if (screen[5]=='8' && screen[6]>'4') rf_level=3;      
+    } else rf_level=0;
+
+    /* Arbitrary units. */
+    if (*units == GRF_Arbitrary) {
+      *level = rf_level;
+      return (GE_NONE);
+    }
+    
+  } else {
+    N6110_SendStatusRequest();
+
+    /* Wait for timeout or other error. */
+    while (timeout != 0 && CurrentRFLevel == -1 ) {
+
+      if (--timeout == 0)
+        return (GE_TIMEOUT);
+
+      usleep (100000);
+    }
+
+    /* Make copy in case it changes. */
+    rf_level = CurrentRFLevel;
+
+    if (rf_level == -1)
+      return (GE_NOLINK);
+
+    /* Now convert between the different units we support. */
+
+    /* Arbitrary units. */
+    if (*units == GRF_Arbitrary) {
+      *level = rf_level;
+      return (GE_NONE);
+    }
+
+    /* CSQ units. */
+    if (*units == GRF_CSQ) {
+
+      if (rf_level <=4)
+        *level = csq_map[rf_level];
+      else
+        *level = 99; /* Unknown/undefined */
+
+      return (GE_NONE);
+    }
+  }
+
+  /* Unit type is one we don't handle so return error */
+  return (GE_INTERNALERROR);
+}
+
+
+GSM_Error N6110_GetBatteryLevel(GSM_BatteryUnits *units, float *level)
+{
+  int timeout=10;
+  int batt_level;
+
+  char screen[NM_MAX_SCREEN_WIDTH];
+
+  CurrentBatteryLevel=-1;
+
+  if (GetModelFeature (FN_NOPOWERFRAME)==F_NOPOWER) {
+
+    if (N6110_NetMonitor(23, screen)!=GE_NONE)
+      return GE_NOLINK;
+    
+    batt_level=4;
+    
+    if (screen[29]=='7') batt_level=3;
+    if (screen[29]=='5') batt_level=2;
+    if (screen[29]=='2') batt_level=1;
+    
+    /* Only units we handle at present are GBU_Arbitrary */
+    if (*units == GBU_Arbitrary) {
+      *level = batt_level;
+      return (GE_NONE);
+    }
+
+    return (GE_INTERNALERROR);    
+    
+  } else {
+    N6110_SendStatusRequest();
+
+    /* Wait for timeout or other error. */
+    while (timeout != 0 && CurrentBatteryLevel == -1 ) {
+
+      if (--timeout == 0)
+        return (GE_TIMEOUT);
+
+      usleep (100000);
+    }
+
+    /* Take copy in case it changes. */
+    batt_level = CurrentBatteryLevel;
+
+    if (batt_level != -1) {
+
+      /* Only units we handle at present are GBU_Arbitrary */
+      if (*units == GBU_Arbitrary) {
+        *level = batt_level;
+        return (GE_NONE);
+      }
+
+      return (GE_INTERNALERROR);
+    }
+    else
+      return (GE_NOLINK);
+  }
+}
+
+GSM_Error N6110_GetPowerSource(GSM_PowerSource *source)
+{
+
+  int timeout=10;
+
+  char screen[NM_MAX_SCREEN_WIDTH];
+
+  CurrentPowerSource=-1;
+
+  if (GetModelFeature (FN_NOPOWERFRAME)==F_NOPOWER) {    
+
+    if (N6110_NetMonitor(20, screen)!=GE_NONE)
+      return GE_NOLINK;
+    
+    CurrentPowerSource=GPS_ACDC;
+
+    if (screen[6]=='x') CurrentPowerSource=GPS_BATTERY;
+
+    *source=CurrentPowerSource;        
+    
+    return GE_NONE;    
+  } else {
+    N6110_SendStatusRequest();
+
+    /* Wait for timeout or other error. */
+    while (timeout != 0 && CurrentPowerSource == -1 ) {
+
+      if (--timeout == 0)
+        return (GE_TIMEOUT);
+
+      usleep (100000);
+    }
+
+    if (CurrentPowerSource != -1) {
+      *source=CurrentPowerSource;
+      return (GE_NONE);
+    }
+    else
+      return (GE_NOLINK);
+  }
+}
+
+void N6110_ReplyGetDisplayStatus(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i;
+
+  for (i=0; i<MessageBuffer[4];i++)
+    if (MessageBuffer[2*i+6]==2)
+      CurrentDisplayStatus|=1<<(MessageBuffer[2*i+5]-1);
+    else
+      CurrentDisplayStatus&= (0xff - (1<<(MessageBuffer[2*i+5]-1)));
+
+#ifdef DEBUG
+  fprintf(stdout, _("Call in progress: %s\n"), CurrentDisplayStatus & (1<<DS_Call_In_Progress)?"on":"off");
+  fprintf(stdout, _("Unknown: %s\n"),          CurrentDisplayStatus & (1<<DS_Unknown)?"on":"off");
+  fprintf(stdout, _("Unread SMS: %s\n"),       CurrentDisplayStatus & (1<<DS_Unread_SMS)?"on":"off");
+  fprintf(stdout, _("Voice call: %s\n"),       CurrentDisplayStatus & (1<<DS_Voice_Call)?"on":"off");
+  fprintf(stdout, _("Fax call active: %s\n"),  CurrentDisplayStatus & (1<<DS_Fax_Call)?"on":"off");
+  fprintf(stdout, _("Data call active: %s\n"), CurrentDisplayStatus & (1<<DS_Data_Call)?"on":"off");
+  fprintf(stdout, _("Keyboard lock: %s\n"),    CurrentDisplayStatus & (1<<DS_Keyboard_Lock)?"on":"off");
+  fprintf(stdout, _("SMS storage full: %s\n"), CurrentDisplayStatus & (1<<DS_SMS_Storage_Full)?"on":"off");
+#endif /* DEBUG */
+
+  CurrentDisplayStatusError=GE_NONE;
+}
+
+GSM_Error N6110_GetDisplayStatus(int *Status) {
+
+  unsigned char req[4]={ N6110_FRAME_HEADER, 0x51 };
+
+  GSM_Error error;
+
+  error=NULL_SendMessageSequence
+    (10, &CurrentDisplayStatusError, 4, 0x0d, req);
+  if (error!=GE_NONE) return error;
+  
+  *Status=CurrentDisplayStatus;
+
+  return GE_NONE;
+}
+
+GSM_Error N6110_DialVoice(char *Number) {
+/* This commented sequence doesn't work on N3210/3310/6210/7110 */
+//  unsigned char req[64]={N6110_FRAME_HEADER, 0x01};
+//  unsigned char req_end[]={0x05, 0x01, 0x01, 0x05, 0x81, 0x01, 0x00, 0x00, 0x01};
+//  int i=0;
+//  req[4]=strlen(Number);
+//  for(i=0; i < strlen(Number) ; i++)
+//   req[5+i]=Number[i];
+//  memcpy(req+5+strlen(Number), req_end, 10);
+//  return NULL_SendMessageSequence
+//    (20, &CurrentDialVoiceError, 13+strlen(Number), 0x01, req);
+
+  unsigned char req[64]={0x00,0x01,0x7c,
+                         0x01}; //call command
+
+  int i=0;                      
+  
+  GSM_Error error;
+
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  for(i=0; i < strlen(Number) ; i++) req[4+i]=Number[i];
+  
+  req[4+i+1]=0;
+  
+  return NULL_SendMessageSequence
+    (20, &CurrentDialVoiceError, 4+strlen(Number)+1, 0x40, req);  
+}
+
+/* Dial a data call - type specifies request to use: 
+     type 0 should normally be used
+     type 1 should be used when calling a digital line - corresponds to ats35=0
+     Maybe one day we'll know what they mean!
+*/
+GSM_Error N6110_DialData(char *Number, char type, void (* callpassup)(char c))
+{
+       unsigned char req[100]   = { N6110_FRAME_HEADER, 0x01 };
+       unsigned char *req_end;
+       unsigned char req_end0[] = { 0x01,  /* make a data call = type 0x01 */
+                                    0x02,0x01,0x05,0x81,0x01,0x00,0x00,0x01,0x02,0x0a,
+                                    0x07,0xa2,0x88,0x81,0x21,0x15,0x63,0xa8,0x00,0x00 };
+       unsigned char req_end1[] = { 0x01,
+                                    0x02,0x01,0x05,0x81,0x01,0x00,0x00,0x01,0x02,0x0a,
+                                    0x07,0xa1,0x88,0x89,0x21,0x15,0x63,0xa0,0x00,0x06,
+                                    0x88,0x90,0x21,0x48,0x40,0xbb };
+       unsigned char req2[]     = { N6110_FRAME_HEADER, 0x42,0x05,0x01,
+                                    0x07,0xa2,0xc8,0x81,0x21,0x15,0x63,0xa8,0x00,0x00,
+                                    0x07,0xa3,0xb8,0x81,0x20,0x15,0x63,0x80,0x01,0x60 };
+       unsigned char req3[]     = { N6110_FRAME_HEADER, 0x42,0x05,0x01,
+                                    0x07,0xa2,0x88,0x81,0x21,0x15,0x63,0xa8,0x00,0x00,
+                                    0x07,0xa3,0xb8,0x81,0x20,0x15,0x63,0x80 };
+       unsigned char req4[]     = { N6110_FRAME_HEADER, 0x42,0x05,0x81,
+                                    0x07,0xa1,0x88,0x89,0x21,0x15,0x63,0xa0,0x00,0x06,
+                                    0x88,0x90,0x21,0x48,0x40,0xbb,0x07,0xa3,0xb8,0x81,
+                                    0x20,0x15,0x63,0x80 };
+
+       int i = 0;
+       u8 size;
+
+       CurrentCallPassup=callpassup;
+
+       switch (type) {
+       case 0:
+               req_end = req_end0;
+               size = sizeof(req_end0);
+               break;
+       case 1:
+               Protocol->SendMessage(sizeof(req3), 0x01, req3);
+               usleep(1000000);
+               Protocol->SendMessage(sizeof(req4), 0x01, req4);
+               usleep(1000000);
+               req_end = req_end1;
+               size = sizeof(req_end1);
+               break;
+       case -1:   /* Just used to set the call passup */
+               return GE_NONE;
+               break;
+       default:
+               req_end = req_end0;
+               size = sizeof(req_end0);
+               break;
+       }
+
+       req[4] = strlen(Number);
+
+       for(i = 0; i < strlen(Number) ; i++)
+               req[5+i] = Number[i];
+
+       memcpy(req + 5 + strlen(Number), req_end, size);
+
+       Protocol->SendMessage(5 + size + strlen(Number), 0x01, req);
+        usleep(1000000);
+
+       if (type != 1) {
+          Protocol->SendMessage(26, 0x01, req2);
+         usleep(1000000);
+        }
+
+       return (GE_NONE);
+}
+
+GSM_Error N6110_GetIncomingCallNr(char *Number)
+{
+
+  if (*CurrentIncomingCall != ' ') {
+    strcpy(Number, CurrentIncomingCall);
+    return GE_NONE;
+  }
+  else
+    return GE_BUSY;
+}
+
+GSM_Error N6110_CancelCall(void)
+{
+//  This frame & method works only on 61xx/51xx
+//  unsigned char req[] = { N6110_FRAME_HEADER, 0x08, 0x00, 0x85};
+//  req[4]=CurrentCallSequenceNumber;
+//  Protocol->SendMessage(6, 0x01, req);
+//  return GE_NONE;
+  GSM_Error error;
+
+  unsigned char req[]={0x00,0x01,0x7c,0x03};
+    
+  /* Checking */
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  return NULL_SendMessageSequence (20, &CurrentDialVoiceError, 4, 0x40, req);   
+}  
+
+void N6110_ReplyEnterSecurityCode(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+    
+  switch(MessageBuffer[3]) {
+
+  case 0x0b:
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Security code accepted.\n"));
+#endif /* DEBUG */
+    CurrentSecurityCodeError = GE_NONE;
+    break;
+
+  default:
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Security code is wrong. You're not my big owner :-)\n"));
+#endif /* DEBUG */
+    CurrentSecurityCodeError = GE_INVALIDSECURITYCODE;
+  }
+}
+
+GSM_Error N6110_EnterSecurityCode(GSM_SecurityCode SecurityCode)
+{
+
+  unsigned char req[15] = { N6110_FRAME_HEADER,
+                            0x0a, /* Enter code request. */
+                            0x00  /* Type of the entered code. */
+                            };
+  int i=0;
+
+  req[4]=SecurityCode.Type;
+
+  for (i=0; i<strlen(SecurityCode.Code);i++)
+    req[5+i]=SecurityCode.Code[i];
+
+  req[5+strlen(SecurityCode.Code)]=0x00;
+  req[6+strlen(SecurityCode.Code)]=0x00;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSecurityCodeError, 7+strlen(SecurityCode.Code), 0x08, req);
+}
+
+void N6110_ReplyGetSecurityCodeStatus(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+    
+  *CurrentSecurityCodeStatus = MessageBuffer[4];
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Security Code status received: "));
+
+  switch(*CurrentSecurityCodeStatus) {
+
+    case GSCT_SecurityCode: fprintf(stdout, _("waiting for Security Code.\n")); break;
+    case GSCT_Pin         : fprintf(stdout, _("waiting for PIN.\n")); break;
+    case GSCT_Pin2        : fprintf(stdout, _("waiting for PIN2.\n")); break;
+    case GSCT_Puk         : fprintf(stdout, _("waiting for PUK.\n")); break;
+    case GSCT_Puk2        : fprintf(stdout, _("waiting for PUK2.\n")); break;
+    case GSCT_None        : fprintf(stdout, _("nothing to enter.\n")); break;
+    default               : fprintf(stdout, _("Unknown!\n"));
+  }
+      
+#endif /* DEBUG */
+
+  CurrentSecurityCodeError = GE_NONE;
+}
+
+GSM_Error N6110_GetSecurityCodeStatus(int *Status)
+{
+
+  unsigned char req[4] = { N6110_FRAME_HEADER,
+                           0x07
+                         };
+
+  CurrentSecurityCodeStatus=Status;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSecurityCodeError, 4, 0x08, req);
+}
+
+void N6110_ReplyGetSecurityCode(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i;
+  
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Security code received: "));
+  switch (MessageBuffer[3]) {
+    case GSCT_SecurityCode: fprintf(stdout, _("Security code"));break;
+    case GSCT_Pin:  fprintf(stdout, _("PIN"));break;
+    case GSCT_Pin2: fprintf(stdout, _("PIN2"));break;
+    case GSCT_Puk:  fprintf(stdout, _("PUK"));break;
+    case GSCT_Puk2: fprintf(stdout, _("PUK2"));break;
+    default: fprintf(stdout, _("unknown !"));break;
+  }
+  if (MessageBuffer[4]==1) {
+    fprintf(stdout, _(" allowed, value \""));
+    if (MessageBuffer[3]==GSCT_SecurityCode) {
+      for (i=0;i<5;i++) {fprintf(stdout, _("%c"), MessageBuffer[5+i]);}
+    }
+    if (MessageBuffer[3]==GSCT_Pin || MessageBuffer[3]==GSCT_Pin2 ||
+       MessageBuffer[3]==GSCT_Puk || MessageBuffer[3]==GSCT_Puk2) {
+      for (i=0;i<4;i++) {fprintf(stdout, _("%c"), MessageBuffer[5+i]);}
+    }
+    fprintf(stdout, _("\""));
+  } else {
+    fprintf(stdout, _(" not allowed"));  
+  }
+  fprintf(stdout, _("\n"));  
+#endif /* DEBUG */
+      
+  if (CurrentSecurityCode->Type==MessageBuffer[3] /* We wanted this code */
+          && MessageBuffer[4]==1) {                      /* It's allowed */
+    if (MessageBuffer[3]==GSCT_SecurityCode) {
+      for (i=0;i<5;i++) {CurrentSecurityCode->Code[i]=MessageBuffer[5+i];}
+      CurrentSecurityCode->Code[5]=0;
+    }
+    if (MessageBuffer[3]==GSCT_Pin || MessageBuffer[3]==GSCT_Pin2 ||
+       MessageBuffer[3]==GSCT_Puk || MessageBuffer[3]==GSCT_Puk2) {
+      for (i=0;i<4;i++) {CurrentSecurityCode->Code[i]=MessageBuffer[5+i];}
+      CurrentSecurityCode->Code[4]=0;
+    }
+    CurrentSecurityCodeError=GE_NONE;
+  } else
+    CurrentSecurityCodeError=GE_INVALIDSECURITYCODE;
+}
+
+GSM_Error N6110_GetSecurityCode(GSM_SecurityCode *SecurityCode)
+{
+
+  unsigned char req[4] = { 0x00,
+                           0x01,0x6e, /* Get code request. */
+                          0x00 };    /* Type of the requested code. */
+
+  GSM_Error error;
+  
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+  
+  req[3]=SecurityCode->Type;
+
+  CurrentSecurityCode=SecurityCode;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSecurityCodeError, 4, 0x40, req);
+}
+
+void N6110_ReplyPlayTone(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: answer for PlayTone frame\n"));      
+#endif
+      
+  CurrentPlayToneError=GE_NONE;      
+}
+
+GSM_Error N6110_PlayTone(int Herz, u8 Volume)
+{
+  unsigned char req[6] = { 0x00,0x01,0x8f,
+                           0x00,   /* Volume */
+                          0x00,   /* HerzLo */
+                          0x00 }; /* HerzHi */
+
+  GSM_Error error;
+
+  /* PlayTone wasn't used earlier */
+  if (CurrentPlayToneError==GE_UNKNOWN) {
+    if (CurrentConnectionType!=GCT_MBUS)
+      CurrentDisableKeepAlive=true;
+
+    error=N6110_EnableExtendedCommands(0x01);
+    if (error!=GE_NONE) return error;
+  }
+
+  /* For Herz==255*255 we have silent */  
+  if (Herz!=255*255) {
+    req[3]=Volume;
+
+    req[5]=Herz%256;
+    req[4]=Herz/256;
+  } else {
+    req[3]=0;
+
+    req[5]=0;
+    req[4]=0;
+  }
+
+#ifdef WIN32
+  /* For Herz==255*255 we have silent and additionaly
+     we wait for phone answer - it's important for MBUS */
+  if (Herz==255*255) {
+    error=NULL_SendMessageSequence
+      (20, &CurrentPlayToneError, 6, 0x40, req);
+
+    CurrentPlayToneError=GE_UNKNOWN;
+    CurrentDisableKeepAlive=false;
+
+    if (error!=GE_NONE) return error;
+  } else {
+    Protocol->SendMessage(6,0x40,req);
+  }
+#else
+  error=NULL_SendMessageSequence
+    (20, &CurrentPlayToneError, 6, 0x40, req);
+
+  /* For Herz==255*255 we wait for phone answer - it's important for MBUS */
+  if (Herz==255*255) {
+    CurrentPlayToneError=GE_UNKNOWN;
+    CurrentDisableKeepAlive=false;
+  }
+  
+  if (error!=GE_NONE) return error;
+
+#endif
+    
+  return(GE_NONE);
+}
+
+void N6110_ReplyGetDateTime(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  if (MessageBuffer[4]==0x01) {
+    DecodeDateTime(MessageBuffer+8, CurrentDateTime);
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Date and time\n"));
+    fprintf(stdout, _("   Time: %02d:%02d:%02d\n"), CurrentDateTime->Hour, CurrentDateTime->Minute, CurrentDateTime->Second);
+    fprintf(stdout, _("   Date: %4d/%02d/%02d\n"), CurrentDateTime->Year, CurrentDateTime->Month, CurrentDateTime->Day);
+#endif /* DEBUG */
+
+    CurrentDateTime->IsSet=true;
+  } else {
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Date and time not set in phone\n"));
+#endif
+
+    CurrentDateTime->IsSet=false;
+  }
+      
+  CurrentDateTimeError=GE_NONE;
+}
+
+GSM_Error N6110_GetDateTime(GSM_DateTime *date_time)
+{
+  return N6110_PrivGetDateTime(date_time,0x11);
+}
+
+GSM_Error N6110_PrivGetDateTime(GSM_DateTime *date_time, int msgtype)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x62};
+
+  CurrentDateTime=date_time;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentDateTimeError, 4, msgtype, req);
+}
+
+void N6110_ReplyGetAlarm(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Alarm\n"));
+  fprintf(stdout, _("   Alarm: %02d:%02d\n"), MessageBuffer[9], MessageBuffer[10]);
+  fprintf(stdout, _("   Alarm is %s\n"), (MessageBuffer[8]==2) ? _("on"):_("off"));
+#endif /* DEBUG */
+
+  CurrentAlarm->Hour=MessageBuffer[9];
+  CurrentAlarm->Minute=MessageBuffer[10];
+  CurrentAlarm->Second=0;
+
+  CurrentAlarm->IsSet=(MessageBuffer[8]==2);
+
+  CurrentAlarmError=GE_NONE;
+}
+
+GSM_Error N6110_GetAlarm(int alarm_number, GSM_DateTime *date_time)
+{
+  return N6110_PrivGetAlarm(alarm_number,date_time,0x11);
+}
+
+GSM_Error N6110_PrivGetAlarm(int alarm_number, GSM_DateTime *date_time, int msgtype)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x6d};
+
+  CurrentAlarm=date_time;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentAlarmError, 4, msgtype, req);
+}
+
+void N6110_ReplyGetSMSCenter(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+  
+  switch (MessageBuffer[3]) {
+
+  case 0x34:
+
+    CurrentMessageCenter->No=MessageBuffer[4];
+    CurrentMessageCenter->Format=MessageBuffer[6];
+    CurrentMessageCenter->Validity=MessageBuffer[8];
+    sprintf(CurrentMessageCenter->Name, "%s", MessageBuffer+33);
+
+    sprintf(CurrentMessageCenter->DefaultRecipient, "%s", GSM_UnpackSemiOctetNumber(MessageBuffer+9,false));
+
+    sprintf(CurrentMessageCenter->Number, "%s", GSM_UnpackSemiOctetNumber(MessageBuffer+21,false));
+      
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SMS Center received:\n"));
+    fprintf(stdout, _("   %d. SMS Center name is %s\n"), CurrentMessageCenter->No, CurrentMessageCenter->Name);
+    fprintf(stdout, _("   SMS Center number is %s\n"), CurrentMessageCenter->Number);
+    fprintf(stdout, _("   Default recipient number is %s\n"), CurrentMessageCenter->DefaultRecipient);
+      
+    fprintf(stdout, _("   SMS Center message format is "));
+
+    switch (CurrentMessageCenter->Format) {
+
+      case GSMF_Text  : fprintf(stdout, _("Text"));   break;
+      case GSMF_Paging: fprintf(stdout, _("Paging")); break;
+      case GSMF_Fax   : fprintf(stdout, _("Fax"));    break;
+      case GSMF_Email : fprintf(stdout, _("Email"));  break;
+      default         : fprintf(stdout, _("Unknown"));
+    }
+
+    fprintf(stdout, "\n");
+
+    fprintf(stdout, _("   SMS Center message validity is "));
+
+    switch (CurrentMessageCenter->Validity) {
+
+      case GSMV_1_Hour  : fprintf(stdout, _("1 hour"));      break;
+      case GSMV_6_Hours : fprintf(stdout, _("6 hours"));     break;
+      case GSMV_24_Hours: fprintf(stdout, _("24 hours"));    break;
+      case GSMV_72_Hours: fprintf(stdout, _("72 hours"));    break;
+      case GSMV_1_Week  : fprintf(stdout, _("1 week"));      break;
+      case GSMV_Max_Time: fprintf(stdout, _("Maximum time"));break;
+      default           : fprintf(stdout, _("Unknown"));
+    }
+
+    fprintf(stdout, "\n");
+
+#endif /* DEBUG */
+
+    CurrentMessageCenterError=GE_NONE;
+
+    break;
+
+  case 0x35:
+
+    /* Number of entries depends on SIM card */
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SMS Center error received:\n"));
+    fprintf(stdout, _("   The request for SMS Center failed.\n"));
+#endif /* DEBUG */
+
+    /* FIXME: appropriate error. */
+    CurrentMessageCenterError=GE_INTERNALERROR;
+
+    break;  
+
+  }
+}
+
+/* This function sends to the mobile phone a request for the SMS Center */
+GSM_Error N6110_GetSMSCenter(GSM_MessageCenter *MessageCenter)
+{
+  unsigned char req[] = { N6110_FRAME_HEADER, 0x33, 0x64,
+                          0x00 /* SMS Center Number. */
+                        };
+
+  req[5]=MessageCenter->No;
+
+  CurrentMessageCenter=MessageCenter;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentMessageCenterError, 6, 0x02, req);
+}
+
+void N6110_ReplySetSMSCenter(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: SMS Center correctly set.\n"));
+#endif
+  CurrentMessageCenterError=GE_NONE;
+}
+
+/* This function set the SMS Center profile on the phone. */
+GSM_Error N6110_SetSMSCenter(GSM_MessageCenter *MessageCenter)
+{
+  unsigned char req[64] = { N6110_FRAME_HEADER, 0x30, 0x64,
+                            0x00, /* SMS Center Number. */
+                            0x00, /* Unknown. */
+                            0x00, /* SMS Message Format. */
+                            0x00, /* Unknown. */
+                            0x00, /* Validity. */
+                            0,0,0,0,0,0,0,0,0,0,0,0, /* Default recipient number */
+                            0,0,0,0,0,0,0,0,0,0,0,0 /* Message Center Number. */
+                            /* Message Center Name. */
+                          };
+
+  req[5]=MessageCenter->No;
+  req[7]=MessageCenter->Format;
+  req[9]=MessageCenter->Validity;
+
+  req[10]=GSM_PackSemiOctetNumber(MessageCenter->DefaultRecipient, req+11, false);
+
+  req[22]=GSM_PackSemiOctetNumber(MessageCenter->Number, req+23, false);
+
+  sprintf(req+34, "%s", MessageCenter->Name);
+
+  CurrentMessageCenter=MessageCenter;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentMessageCenterError, 35+strlen(MessageCenter->Name), 0x02, req);
+}
+
+void N6110_ReplyGetSMSStatus(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x37:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SMS Status Received\n"));
+    fprintf(stdout, _("   The number of messages: %d\n"), MessageBuffer[10]);
+    fprintf(stdout, _("   Unread messages: %d\n"), MessageBuffer[11]);
+#endif /* DEBUG */
+
+    CurrentSMSStatus->UnRead = MessageBuffer[11];
+    CurrentSMSStatus->Number = MessageBuffer[10];
+    
+    CurrentSMSStatusError = GE_NONE;
+    break;
+
+  case 0x38:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SMS Status error, probably not authorized by PIN\n"));
+#endif /* DEBUG */
+
+    CurrentSMSStatusError = GE_INTERNALERROR;
+    break;
+         
+  }
+}
+
+GSM_Error N6110_GetSMSStatus(GSM_SMSStatus *Status)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x36, 0x64};
+
+  CurrentSMSStatus = Status;
+
+  return NULL_SendMessageSequence
+    (10, &CurrentSMSStatusError, 5, 0x14, req);
+}
+
+GSM_Error N6110_GetSMSFolders ( GSM_SMSFolders *folders)
+{
+  folders->number=2;
+
+  strcpy(folders->Folder[0].Name,"Inbox");
+  strcpy(folders->Folder[1].Name,"Outbox");
+  
+  return GE_NONE;
+}
+
+GSM_Error N6110_GetIMEI(char *imei)
+{
+  if (strlen(Current_IMEI)>0) {
+    strncpy (imei, Current_IMEI, GSM_MAX_IMEI_LENGTH);
+    return (GE_NONE);
+  }
+  else
+    return (GE_TRYAGAIN);
+}
+
+GSM_Error N6110_GetRevision(char *revision)
+{
+
+  if (strlen(Current_Revision)>0) {
+    strncpy (revision, Current_Revision, GSM_MAX_REVISION_LENGTH);
+    return (GE_NONE);
+  }
+  else
+    return (GE_TRYAGAIN);
+}
+
+GSM_Error N6110_GetModel(char *model)
+{
+  if (strlen(Current_Model)>0) {
+    strncpy (model, Current_Model, GSM_MAX_MODEL_LENGTH);
+    return (GE_NONE);
+  }
+  else
+    return (GE_TRYAGAIN);
+}
+
+void N6110_ReplySetDateTime(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[4]) {
+
+    case 0x01:
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Date and time set correctly\n"));
+#endif /* DEBUG */
+      CurrentSetDateTimeError=GE_NONE;
+      break;
+      
+    default:
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Date and time setting error\n"));
+#endif /* DEBUG */
+      CurrentSetDateTimeError=GE_INVALIDDATETIME;
+
+  }
+}
+
+/* Needs SIM card with PIN in phone */
+GSM_Error N6110_SetDateTime(GSM_DateTime *date_time)
+{
+  return N6110_PrivSetDateTime(date_time,0x11);
+}
+
+/* Needs SIM card with PIN in phone */
+GSM_Error N6110_PrivSetDateTime(GSM_DateTime *date_time, int msgtype)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                         0x60, /* set-time subtype */
+                         0x01, 0x01, 0x07, /* unknown */
+                         0x00, 0x00, /* Year (0x07cf = 1999) */
+                         0x00, 0x00, /* Month Day */
+                         0x00, 0x00, /* Hours Minutes */
+                         0x00 /* Unknown, but not seconds - try 59 and wait 1 sec. */
+                       };
+
+  EncodeDateTime(req+7, date_time);
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSetDateTimeError, 14, msgtype, req);
+}
+
+void N6110_ReplySetAlarm(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[4]) {
+
+    case 0x01:
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Alarm set correctly\n"));
+#endif /* DEBUG */
+      CurrentSetAlarmError=GE_NONE;
+      break;
+      
+    default:
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Alarm setting error\n"));
+#endif /* DEBUG */
+      CurrentSetAlarmError=GE_INVALIDDATETIME;
+
+  }
+}
+
+/* FIXME: we should also allow to set the alarm off :-) */
+GSM_Error N6110_SetAlarm(int alarm_number, GSM_DateTime *date_time)
+{
+  return N6110_PrivSetAlarm(alarm_number,date_time, 0x11);
+}
+
+/* FIXME: we should also allow to set the alarm off :-) */
+GSM_Error N6110_PrivSetAlarm(int alarm_number, GSM_DateTime *date_time, int msgtype)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                         0x6b, /* set-alarm subtype */
+                         0x01, 0x20, 0x03, /* unknown */
+                         0x02,       /* should be alarm on/off, but it don't works */
+                         0x00, 0x00, /* Hours Minutes */
+                         0x00 /* Unknown, but not seconds - try 59 and wait 1 sec. */
+                       };
+
+  req[8] = date_time->Hour;
+  req[9] = date_time->Minute;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentSetAlarmError, 11, msgtype, req);
+}
+
+void N6110_ReplyGetMemoryLocation(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  /* Hopefully is 64 larger as FB38_MAX* / N6110_MAX* */
+  char model[64];
+
+  int i, tmp, count;
+    
+  switch (MessageBuffer[3]) {
+
+  case 0x02:
+
+    CurrentPhonebookEntry->Empty = true;
+
+    count=MessageBuffer[5];
+         
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Phonebook entry received:\n"));
+    fprintf(stdout, _("   Name: "));
+
+    for (tmp=0; tmp <count; tmp++)
+    {
+      if (MessageBuffer[6+tmp]==1) fprintf(stdout, "%c", '~'); else //enables/disables blinking
+      if (MessageBuffer[6+tmp]==0) fprintf(stdout, "%c", '`'); else //hides rest ot contents
+      fprintf(stdout, "%c", MessageBuffer[6+tmp]);
+    }
+
+    fprintf(stdout, "\n");
+#endif /* DEBUG */
+
+    while (N6110_GetModel(model)  != GE_NONE)
+      sleep(1);
+        
+    if (GetModelFeature (FN_PHONEBOOK)==F_PBK33SIM ||
+        GetModelFeature (FN_PHONEBOOK)==F_PBK33INT) {//pbk with Unicode
+      DecodeUnicode (CurrentPhonebookEntry->Name, MessageBuffer+6, count/2);
+      CurrentPhonebookEntry->Name[count/2] = 0x00;
+    } else {
+      memcpy(CurrentPhonebookEntry->Name, MessageBuffer + 6, count);
+      CurrentPhonebookEntry->Name[count] = 0x00;
+    }
+
+    CurrentPhonebookEntry->Empty = false;
+
+    for (tmp=0; tmp <count; tmp++)
+    {
+      if (GetModelFeature (FN_PHONEBOOK)==F_PBK33INT ||
+          GetModelFeature (FN_PHONEBOOK)==F_PBK33SIM) {//pbk with Unicode
+        /* We check only 1'st, 3'rd, ... char */
+        if (tmp%2!=0 && MessageBuffer[6+tmp]==1) CurrentPhonebookEntry->Name[tmp/2]='~'; //enables/disables blinking
+        if (tmp%2!=0 && MessageBuffer[6+tmp]==0) CurrentPhonebookEntry->Name[tmp/2]='`'; //hides rest ot contents
+      } else {
+        if (MessageBuffer[6+tmp]==1) CurrentPhonebookEntry->Name[tmp]='~'; //enables/disables blinking
+        if (MessageBuffer[6+tmp]==0) CurrentPhonebookEntry->Name[tmp]='`'; //hides rest ot contents
+      }
+    }
+
+    i=7+count;
+    count=MessageBuffer[6+count];
+
+#ifdef DEBUG
+    fprintf(stdout, _("   Number: "));
+
+    for (tmp=0; tmp <count; tmp++)
+      fprintf(stdout, "%c", MessageBuffer[i+tmp]);
+
+    fprintf(stdout, "\n");
+#endif /* DEBUG */
+
+    memcpy(CurrentPhonebookEntry->Number, MessageBuffer + i, count);
+    CurrentPhonebookEntry->Number[count] = 0x00;
+    CurrentPhonebookEntry->Group = MessageBuffer[i+count];
+      
+    /* Phone doesn't have entended phonebook */
+    CurrentPhonebookEntry->SubEntriesCount = 0;
+
+    /* But for these memories data is saved and we can save it using 7110/6210 style */
+    if (CurrentPhonebookEntry->MemoryType==GMT_DC ||
+        CurrentPhonebookEntry->MemoryType==GMT_RC ||
+        CurrentPhonebookEntry->MemoryType==GMT_MC) {
+        CurrentPhonebookEntry->SubEntriesCount = 1;
+        CurrentPhonebookEntry->SubEntries[0].EntryType=N7110_ENTRYTYPE_DATE;
+        CurrentPhonebookEntry->SubEntries[0].NumberType=0;
+        CurrentPhonebookEntry->SubEntries[0].BlockNumber=1;
+        DecodeDateTime(MessageBuffer+(i+count+2),&CurrentPhonebookEntry->SubEntries[0].data.Date);
+
+#ifdef DEBUG
+      fprintf(stdout, _("   Date: "));
+      fprintf(stdout, "%02u.%02u.%04u\n",
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Day,
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Month,
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Year);
+      fprintf(stdout, _("   Time: "));
+      fprintf(stdout, "%02u:%02u:%02u\n",
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Hour,
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Minute,
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Second);
+#endif /* DEBUG */
+
+      /* These values are set, when date and time unavailable in phone.
+         Values from 3310 - in other can be different */
+      if (CurrentPhonebookEntry->SubEntries[0].data.Date.Day==20 &&
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Month==1 &&
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Year==2118 &&
+          CurrentPhonebookEntry->SubEntries[0].data.Date.Hour==3 &&
+         CurrentPhonebookEntry->SubEntries[0].data.Date.Minute==14 &&
+         CurrentPhonebookEntry->SubEntries[0].data.Date.Second==7)
+          CurrentPhonebookEntry->SubEntriesCount = 0;
+    }
+
+    /* Signal no error to calling code. */
+    CurrentPhonebookError = GE_NONE;
+
+    break;
+
+  case 0x03:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Phonebook read entry error received:\n"));
+#endif /* DEBUG */
+
+    switch (MessageBuffer[4]) {
+
+      case 0x7d:
+#ifdef DEBUG
+       fprintf(stdout, _("   Invalid memory type!\n"));
+#endif /* DEBUG */
+       CurrentPhonebookError = GE_INVALIDMEMORYTYPE;
+       break;
+
+      default:
+#ifdef DEBUG
+       fprintf(stdout, _("   Unknown error!\n"));
+#endif /* DEBUG */
+       CurrentPhonebookError = GE_INTERNALERROR;
+    }
+
+    break;
+
+  }
+}
+
+/* Routine to get specifed phone book location.  Designed to be called by
+   application.  Will block until location is retrieved or a timeout/error
+   occurs. */
+GSM_Error N6110_GetMemoryLocation(GSM_PhonebookEntry *entry)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x01, 0x00, 0x00, 0x00};
+
+  CurrentPhonebookEntry = entry;
+
+  req[4] = N6110_GetMemoryType(entry->MemoryType);
+  req[5] = entry->Location;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentPhonebookError, 7, 0x03, req);
+}
+
+void N6110_ReplyWritePhonebookLocation(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x05:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Phonebook written correctly.\n"));
+#endif /* DEBUG */
+    CurrentPhonebookError = GE_NONE;
+    break;
+
+  case 0x06:
+
+    switch (MessageBuffer[4]) {
+      /* FIXME: other errors? When I send the phonebook with index of 350 it
+         still report error 0x7d :-( */
+      case 0x7d:
+#ifdef DEBUG
+        fprintf(stdout, _("Message: Phonebook not written - name is too long.\n"));
+#endif /* DEBUG */
+       CurrentPhonebookError = GE_PHBOOKNAMETOOLONG;
+       break;
+
+      default:
+#ifdef DEBUG
+       fprintf(stdout, _("   Unknown error!\n"));
+#endif /* DEBUG */
+       CurrentPhonebookError = GE_INTERNALERROR;
+    }
+  }
+}
+
+/* Routine to write phonebook location in phone. Designed to be called by
+   application code. Will block until location is written or timeout
+   occurs. */
+GSM_Error N6110_WritePhonebookLocation(GSM_PhonebookEntry *entry)
+{
+  unsigned char req[128] = { N6110_FRAME_HEADER, 0x04, 0x00, 0x00 };
+  int i=0, current=0;
+
+  req[4] = N6110_GetMemoryType(entry->MemoryType);
+  req[5] = entry->Location;
+
+  current=7;
+
+  if (GetModelFeature (FN_PHONEBOOK)==F_PBK33INT ||
+      GetModelFeature (FN_PHONEBOOK)==F_PBK33SIM) {
+
+     req[6] = strlen(entry->Name)*2;
+
+     EncodeUnicode (req+current,entry->Name ,strlen(entry->Name));
+     
+     for (i=0; i<strlen(entry->Name); i++)
+     {
+       /* here we encode "special" chars */
+       if (entry->Name[i]=='~') req[current+i*2]=1; //enables/disables blinking
+       if (entry->Name[i]=='`') req[current+i*2]=0; //hides rest ot contents
+     }
+
+     current+=strlen(entry->Name)*2;
+  } else {
+
+    req[6] = strlen(entry->Name);
+
+    for (i=0; i<strlen(entry->Name); i++)
+    {
+      req[current+i] = entry->Name[i];
+
+      /* here we encode "special" chars */
+      if (entry->Name[i]=='~') req[current+i]=1; //enables/disables blinking
+      if (entry->Name[i]=='`') req[current+i]=0; //hides rest ot contents
+    }
+
+    current+=strlen(entry->Name);
+  }
+
+  req[current++]=strlen(entry->Number);
+
+  for (i=0; i<strlen(entry->Number); i++)
+    req[current+i] = entry->Number[i];
+
+  current+=strlen(entry->Number);
+
+  /* Jano: This allow to save 14 characters name into SIM memory, when
+     No Group is selected. */
+  if (entry->Group == 5)
+    req[current++]=0xff;
+  else
+    req[current++]=entry->Group;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentPhonebookError, current, 0x03, req);
+}
+
+void N6110_ReplyNetmonitor(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch(MessageBuffer[3]) {
+
+    case 0x00:
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Netmonitor correctly set.\n"));
+#endif /* DEBUG */
+      CurrentNetmonitorError=GE_NONE;  
+      break;
+      
+    default:
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Netmonitor menu %d received:\n"), MessageBuffer[3]);
+      fprintf(stdout, "%s\n", MessageBuffer+4);
+#endif /* DEBUG */
+
+      strcpy(CurrentNetmonitor, MessageBuffer+4);
+
+      CurrentNetmonitorError=GE_NONE;  
+  }
+}
+
+GSM_Error N6110_NetMonitor(unsigned char mode, char *Screen)
+{
+  unsigned char req[] = { 0x00, 0x01, 0x7e, 0x00 };
+  
+  GSM_Error error;
+  
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  CurrentNetmonitor=Screen;
+
+  req[3]=mode;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentNetmonitorError, 4, 0x40, req);
+}
+
+/* Doesn't work in N3210. */
+/* In other allow to access phone menu without SIM card (just send any sequence) */
+GSM_Error N6110_SendDTMF(char *String)
+{
+  unsigned char req[64] = { N6110_FRAME_HEADER, 0x50,
+                            0x00 /* Length of DTMF string. */
+                          };
+                         
+  u8 length=strlen(String);
+
+  if (length>59) length=59;
+  
+  req[4] = length;
+  
+  memcpy(req+5,String,length);
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSendDTMFError, 5+length, 0x01, req);
+}
+
+void N6110_ReplyGetSpeedDial(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x17:
+
+    switch (MessageBuffer[4]) {
+      case 0x02: CurrentSpeedDialEntry->MemoryType = GMT_ME;
+      default  : CurrentSpeedDialEntry->MemoryType = GMT_SM;
+    }
+      
+    CurrentSpeedDialEntry->Location = MessageBuffer[5];
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Speed dial entry received:\n"));
+    fprintf(stdout, _("   Location: %d\n"), CurrentSpeedDialEntry->Location);
+    fprintf(stdout, _("   MemoryType: %s\n"), N6110_MemoryType_String[CurrentSpeedDialEntry->MemoryType]);
+    fprintf(stdout, _("   Number: %d\n"), CurrentSpeedDialEntry->Number);
+#endif /* DEBUG */
+
+    CurrentSpeedDialError=GE_NONE;
+    break;
+
+  case 0x18:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Speed dial entry error\n"));
+#endif /* DEBUG */
+    CurrentSpeedDialError=GE_INVALIDSPEEDDIALLOCATION;
+    break;
+
+  }
+}
+
+GSM_Error N6110_GetSpeedDial(GSM_SpeedDial *entry)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x16,
+                          0x00  /* The number of speed dial. */
+                        };
+
+  CurrentSpeedDialEntry = entry;
+
+  req[4] = entry->Number;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSpeedDialError, 5, 0x03, req);
+}
+
+void N6110_ReplySetSpeedDial(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x1a:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Speed dial entry set.\n"));
+#endif /* DEBUG */
+    CurrentSpeedDialError=GE_NONE;
+    break;
+
+  case 0x1b:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Speed dial entry setting error.\n"));
+#endif /* DEBUG */
+    CurrentSpeedDialError=GE_INVALIDSPEEDDIALLOCATION;
+    break;
+
+  }
+}
+
+GSM_Error N6110_SetSpeedDial(GSM_SpeedDial *entry)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x19,
+                          0x00, /* Number */
+                          0x00, /* Memory Type */
+                          0x00  /* Location */
+                        };
+
+  req[4] = entry->Number;
+
+  switch (entry->MemoryType) {
+    case GMT_ME: req[5] = 0x02;
+    default    : req[5] = 0x03;
+  }
+
+  req[6] = entry->Location;
+
+  return NULL_SendMessageSequence
+    (20, &CurrentSpeedDialError, 7, 0x03, req);
+}
+
+/* This function finds parts of SMS in frame used in new Nokia phones
+   in internal protocols (they're coded according to GSM 03.40), copies them
+   to GSM_ETSISMSMessage and calls GSM_DecodeETSISMS to decode
+   GSM_ETSISMSMessage to GSM_SMSMessage structure */
+GSM_Error GSM_DecodeNokiaSMSFrame(GSM_SMSMessage *SMS, unsigned char *req, int length)
+{
+  SMS_MessageType PDU=SMS_Deliver;
+  GSM_ETSISMSMessage ETSI;
+  int offset=0,i;
+
+  ETSI.firstbyte=req[12];
+
+  /* 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;
+
+  switch (PDU) {
+    case SMS_Submit       : offset=5;break;
+    case SMS_Deliver      : offset=4;break;
+    case SMS_Status_Report: offset=3;break;
+    default:                break;
+  }
+
+  for (i=0;i<req[0]+1;i++)
+    ETSI.SMSCNumber[i]=req[i];
+
+  for (i=0;i<((req[12+offset]+1)/2+1)+1;i++)
+    ETSI.Number[i]=req[i+12+offset];
+
+  switch (PDU) {
+    case SMS_Submit:
+      ETSI.TPDCS=req[10+offset];
+      ETSI.TPUDL=req[11+offset];
+      ETSI.TPVP=0;  //no support for now
+      ETSI.TPPID=0; //no support for now
+      for(i=31+offset;i<length;i++)
+        ETSI.MessageText[i-31-offset]=req[i];
+      break;
+    case SMS_Deliver:
+      ETSI.TPDCS=req[10+offset];
+      ETSI.TPUDL=req[11+offset];
+      ETSI.TPPID=0; //no support for now
+      for(i=31+offset;i<length;i++)
+        ETSI.MessageText[i-31-offset]=req[i];
+      for(i=0;i<7;i++)
+        ETSI.DeliveryDateTime[i]=req[i+24+offset];
+      break;
+    case SMS_Status_Report:
+      for(i=0;i<7;i++)
+        ETSI.DeliveryDateTime[i]=req[i+24+offset];
+      ETSI.TPStatus=req[14];
+      for(i=0;i<7;i++)
+        ETSI.SMSCDateTime[i]=req[i+34];
+      break;
+    default:
+      break;
+  }
+
+  GSM_DecodeETSISMS(SMS, &ETSI);
+
+  SMS->Name[0]=0;
+
+  return GE_NONE;
+}
+
+void N6110_ReplyGetSMSMessage(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int offset;
+  
+  switch (MessageBuffer[3]) {
+
+  case 0x08:
+
+    switch (MessageBuffer[7]) {
+
+      case 0x00:
+       CurrentSMSMessage->Type = GST_SMS;
+        CurrentSMSMessage->folder=GST_INBOX;
+       offset=4;
+        break;
+
+      case 0x01:
+        CurrentSMSMessage->Type = GST_DR;
+       CurrentSMSMessage->folder=GST_INBOX;
+       offset=3;
+        break;
+
+      case 0x02:
+       CurrentSMSMessage->Type = GST_SMS;
+       CurrentSMSMessage->folder=GST_OUTBOX;
+       offset=5;
+        break;
+
+      default:
+        CurrentSMSMessage->Type = GST_UN;
+       offset=4;
+        break;
+
+    }
+
+    /* Field Short Message Status - MessageBuffer[4] seems not to be
+       compliant with GSM 07.05 spec.
+       Meaning     Nokia protocol      GMS spec
+       ----------------------------------------------------
+       MO Sent     0x05                        0x07 or 0x01
+       MO Not sent 0x07                        0x06 or 0x00
+       MT Read    0x01                 0x05 or 0x01
+       MT Not read 0x03                        0x04 or 0x00
+       ----------------------------------------------------
+       See GSM 07.05 section 2.5.2.6 and correct me if I'm wrong.
+       
+                                        Pawel Kot */
+
+    if (MessageBuffer[4] & 0x02) CurrentSMSMessage->Status = GSS_NOTSENTREAD;
+                            else CurrentSMSMessage->Status = GSS_SENTREAD;
+
+#ifdef DEBUG
+    fprintf(stdout, _("Number: %d\n"), MessageBuffer[6]);
+
+    if (CurrentSMSMessage->folder!=1) { //GST_OUTBOX
+      fprintf(stdout, _("Message: Received SMS (mobile terminated)\n"));
+    } else {
+      fprintf(stdout, _("Message: Outbox message (mobile originated)\n"));
+    }
+
+    if (CurrentSMSMessage->Type == GST_DR) fprintf(stdout, _("   Delivery Report\n"));
+    if (CurrentSMSMessage->Type == GST_UN) fprintf(stdout, _("   Unknown type\n"));
+
+    if (CurrentSMSMessage->folder==1) { //GST_OUTBOX
+      if (CurrentSMSMessage->Status) fprintf(stdout, _("   Sent\n"));
+                                else fprintf(stdout, _("   Not sent\n"));
+    } else {
+      if (CurrentSMSMessage->Status) fprintf(stdout, _("   Read\n"));
+                                else fprintf(stdout, _("   Not read\n"));
+    }
+#endif
+
+    CurrentSMSPointer=GSM_DecodeNokiaSMSFrame(CurrentSMSMessage, MessageBuffer+8, MessageLength-8);
+
+    CurrentSMSMessage->MemoryType = MessageBuffer[5];
+    CurrentSMSMessage->MessageNumber = MessageBuffer[6];
+    /* Signal no error to calling code. */
+    CurrentSMSMessageError = GE_NONE;
+
+#ifdef DEBUG
+    fprintf(stdout, "\n");
+#endif
+
+    break;
+
+  case 0x09:
+
+    /* We have requested invalid or empty location. */
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SMS reading failed\n"));
+
+    switch (MessageBuffer[4]) {
+      case 0x02:
+        fprintf(stdout, _("   Invalid location!\n"));break;
+      case 0x07:
+       fprintf(stdout, _("   Empty SMS location.\n"));break;
+      case 0x0c:
+       fprintf(stdout, _("   No access to memory (no PIN on card ?)\n"));break;
+      default:      
+        fprintf(stdout, _("   Error code %i - please report it \n"),MessageBuffer[4]);break;
+    }
+#endif /* DEBUG */
+
+    switch (MessageBuffer[4]) {
+      case 0x02:CurrentSMSMessageError = GE_INVALIDSMSLOCATION;break;
+      case 0x07:CurrentSMSMessageError = GE_EMPTYSMSLOCATION;break;
+      case 0x0c:CurrentSMSMessageError = GE_NOACCESS;break;
+      default  :CurrentSMSMessageError = GE_UNKNOWN;break;
+    }
+
+    break;
+
+  }
+}
+
+GSM_Error N6110_GetSMSMessage(GSM_SMSMessage *message)
+{
+
+  unsigned char req[] = { N6110_FRAME_HEADER,
+                          0x07,
+                          0x02, /* Unknown */
+                          0x00, /* Location */
+                          0x01, 0x64};
+
+  int timeout = 60;
+
+  /* State machine code writes data to these variables when it comes in. */
+
+  CurrentSMSMessage = message;
+  CurrentSMSMessageError = GE_BUSY;
+
+  req[5] = message->Location;
+
+  /* Send request */
+  Protocol->SendMessage(8, 0x02, req);
+
+  /* Wait for timeout or other error. */
+  while (timeout != 0 && (CurrentSMSMessageError == GE_BUSY || CurrentSMSMessageError == GE_SMSWAITING)) {
+
+    if (--timeout == 0)
+      return (GE_TIMEOUT);
+
+    usleep (100000);
+  }
+
+  return (CurrentSMSMessageError);
+}
+
+void N6110_ReplyDeleteSMSMessage(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: SMS deleted successfully.\n"));
+#endif /* DEBUG */
+
+  CurrentSMSMessageError = GE_NONE;    
+}
+
+GSM_Error N6110_DeleteSMSMessage(GSM_SMSMessage *message)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x0a, 0x02, 0x00};
+
+  req[5] = message->Location;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentSMSMessageError, 6, 0x14, req);
+}
+
+/* FIXME: do we need more than SMS_Submit and SMS_Deliver ? */
+GSM_Error GSM_EncodeNokiaSMSFrame(GSM_SMSMessage *SMS, unsigned char *req, int *length, SMS_MessageType PDU)
+{
+  GSM_ETSISMSMessage ETSI;
+  int i,offset=0;
+
+  GSM_EncodeETSISMS(SMS, &ETSI, PDU, length);
+
+  /* Cleaning */
+  for (i=0;i<36;i++) req[i]=0;
+
+  req[12]=ETSI.firstbyte;
+
+  for (i=0;i<ETSI.SMSCNumber[0]+1;i++)
+    req[i]=ETSI.SMSCNumber[i];
+
+  switch (PDU) {
+    case SMS_Submit:
+      offset=5;
+      for (i=0;i<((ETSI.Number[0]+1)/2+1)+1;i++)
+        req[i+12+offset]=ETSI.Number[i];
+      req[10+offset]=ETSI.TPDCS;
+      req[11+offset]=ETSI.TPUDL;
+      req[24+offset]=ETSI.TPVP;
+#ifdef DEBUG
+//      fprintf(stdout,_("   First byte: %02x\n"),ETSI.firstbyte);
+//      fprintf(stdout,_("   TP-VP: %02x\n"),ETSI.TPVP);
+//      fprintf(stdout,_("   TP-DCS: %02x\n"),ETSI.TPDCS);
+#endif
+//    req[]=ETSI.TPPID;
+      for(i=0;i<*length;i++)
+        req[i+31+offset]=ETSI.MessageText[i];
+      break;
+
+    case SMS_Deliver:
+      offset=4;
+      for (i=0;i<((ETSI.Number[0]+1)/2+1)+1;i++)
+        req[i+12+offset]=ETSI.Number[i];
+      req[10+offset]=ETSI.TPDCS;
+      req[11+offset]=ETSI.TPUDL;
+//    req[]=ETSI.TPPID;
+      for(i=0;i<*length;i++)
+        req[i+31+offset]=ETSI.MessageText[i];
+      for (i=0;i<7;i++)
+        req[24+offset+i]=ETSI.DeliveryDateTime[i];
+      break;
+    default:
+      break;
+  }
+  
+  *length=*length+offset;
+  
+  return GE_NONE;
+}
+
+void N6110_ReplySendSMSMessage(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+    
+  switch (MessageBuffer[3]) {
+
+  /* SMS message correctly sent to the network */
+  case 0x02:
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SMS Message correctly sent.\n"));
+#endif /* DEBUG */
+    CurrentSMSMessageError = GE_SMSSENDOK;
+    break;
+
+  /* SMS message send to the network failed */
+  case 0x03:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Sending SMS Message failed, error: %i"),MessageBuffer[6]);
+      
+    switch (MessageBuffer[6]) {
+      case 1: fprintf(stdout,_(" (info \"Number not in use\")"));break;
+      case 21: fprintf(stdout,_(" (info \"Message not sent this time\")"));break;
+      case 28: fprintf(stdout,_(" (info \"Number not in use\")"));break;
+      case 38: fprintf(stdout,_(" (info \"Message not sent this time\")"));break;       case 50: fprintf(stdout,_(" (info \"Check operator services\")"));break;       
+      case 96: fprintf(stdout,_(" (info \"Message sending failed\")"));break;  
+      case 111: fprintf(stdout,_(" (info \"Message sending failed\")"));break; 
+      case 166: fprintf(stdout,_(" (info \"Message sending failed\")"));break; 
+      case 178: fprintf(stdout,_(" (info \"Message sending failed\")"));break; 
+      case 252: fprintf(stdout,_(" (info \"Message sending failed\")"));break;        case 253: fprintf(stdout,_(" (info \"Message sending failed\")"));break; 
+    }
+
+    fprintf(stdout,_("\n   For more details with errors see netmonitor manual (test 65) on www.marcin-wiacek.topnet.pl"));
+    fprintf(stdout,_("\n   If know their meaning, GSM specs decribing them, contact with me on marcin-wiacek@topnet.pl. THX\n"));
+#endif /* DEBUG */
+
+    CurrentSMSMessageError = GE_SMSSENDFAILED;
+    break;
+
+  }
+}
+
+GSM_Error N6110_SendSMSMessage(GSM_SMSMessage *SMS)
+{
+  GSM_Error error;
+
+  unsigned char req[256] = {
+    N6110_FRAME_HEADER,
+    0x01, 0x02, 0x00, /* SMS send request*/
+  };
+
+  int length;
+
+  error=GSM_EncodeNokiaSMSFrame(SMS, req+6, &length, SMS_Submit);    
+  if (error != GE_NONE) return error;
+
+  return NULL_SendMessageSequence
+    (200, &CurrentSMSMessageError, 42+length, 0x02, req);
+}
+
+void N6110_ReplySaveSMSMessage(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  case 0x05:
+      
+#ifdef DEBUG
+    fprintf(stdout, _("SMS Message stored at %d\n"), MessageBuffer[5]);
+#endif
+      
+    CurrentSMSMessage->MessageNumber=MessageBuffer[5];
+      
+    CurrentSMSMessageError = GE_NONE;
+    break;
+
+  case 0x06:
+#ifdef DEBUG
+    fprintf(stdout, _("SMS saving failed\n"));
+    switch (MessageBuffer[4]) {
+      case 0x02:fprintf(stdout, _("   All locations busy.\n"));break;
+      case 0x03:fprintf(stdout, _("   Invalid location!\n"));break;
+      default  :fprintf(stdout, _("   Unknown error.\n"));break;
+    }
+#endif      
+      
+    switch (MessageBuffer[4]) {
+      case 0x02:CurrentSMSMessageError = GE_MEMORYFULL;break;
+      case 0x03:CurrentSMSMessageError = GE_INVALIDSMSLOCATION;break;
+      default  :CurrentSMSMessageError = GE_UNKNOWN;break;
+    }
+  }
+}
+
+/* GST_DR and GST_UN not supported ! */
+GSM_Error N6110_SaveSMSMessage(GSM_SMSMessage *SMS)
+{
+  unsigned char req[256] = {
+    N6110_FRAME_HEADER, 0x04, /* SMS save request*/
+    0x00, /* SMS Status. Different for Inbox and Outbox */
+    0x02, /* ?? */
+    0x00, /* SMS Location */
+    0x02, /* SMS Type */
+  };
+
+  int length;
+  SMS_MessageType PDU;
+  GSM_Error error;
+
+  if (SMS->Location) req[6] = SMS->Location;
+    
+  if (SMS->folder==0) { /*Inbox*/
+    req[4]=1;      /* SMS Status */
+    req[7] = 0x00; /* SMS Type */
+    PDU=SMS_Deliver;
+  } else {
+    req[4]=5;      /* SMS Status */
+    req[7] = 0x02; /* SMS Type */
+    PDU=SMS_Submit;
+  }
+  
+  if (SMS->Status == GSS_NOTSENTREAD) req[4] |= 0x02;  
+
+  error=GSM_EncodeNokiaSMSFrame(SMS, req+8, &length, PDU);  
+  if (error != GE_NONE) return error;
+
+  CurrentSMSMessage = SMS;
+
+  return NULL_SendMessageSequence
+    (70, &CurrentSMSMessageError, 39+length, 0x14, req);
+}
+
+void N6110_ReplySetCellBroadcast(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: Cell Broadcast enabled/disabled successfully.\n")); fflush (stdout);
+#endif
+
+  CurrentCBError = GE_NONE;
+}
+
+/* Enable and disable Cell Broadcasting */
+GSM_Error N6110_EnableCellBroadcast(void)
+{
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x20,
+                         0x01, 0x01, 0x00, 0x00, 0x01, 0x01};
+
+#ifdef DEBUG
+  fprintf (stdout,"Enabling CB\n");
+#endif
+
+  CurrentCBMessage = (GSM_CBMessage *)malloc(sizeof (GSM_CBMessage));
+  CurrentCBMessage->Channel = 0;
+  CurrentCBMessage->New = false;
+  strcpy (CurrentCBMessage->Message,"");
+
+  return NULL_SendMessageSequence
+    (10, &CurrentCBError, 10, 0x02, req);
+}
+
+
+GSM_Error N6110_DisableCellBroadcast(void)
+{
+  /* Should work, but not tested fully */
+
+  unsigned char req[] = {N6110_FRAME_HEADER, 0x20,
+                         0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /*VERIFY*/
+
+  return NULL_SendMessageSequence
+    (10, &CurrentCBError, 10, 0x02, req);
+}
+
+void N6110_ReplyReadCellBroadcast(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i, tmp;
+  unsigned char output[160];
+  
+  CurrentCBMessage->Channel = MessageBuffer[7];
+  CurrentCBMessage->New = true;
+  tmp=GSM_UnpackEightBitsToSeven(0, MessageBuffer[9], MessageBuffer[9], MessageBuffer+10, output);
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: CB received.\n")); fflush (stdout);
+
+  fprintf(stdout, _("Message: channel number %i\n"),MessageBuffer[7]);
+
+  fflush (stdout);
+
+  for (i=0; i<tmp;i++) {
+    fprintf(stdout, "%c", DecodeWithDefaultAlphabet(output[i]));
+  }
+
+  fprintf(stdout, "\n");
+#endif
+   
+  for (i=0; i<tmp; i++) {
+    CurrentCBMessage->Message[i] = DecodeWithDefaultAlphabet(output[i]);
+  }
+  CurrentCBMessage->Message[i]=0;
+}
+
+GSM_Error N6110_ReadCellBroadcast(GSM_CBMessage *Message)
+{
+#ifdef DEBUG
+   fprintf(stdout,"Reading CB\n");
+#endif
+
+  if (CurrentCBMessage != NULL) 
+  {
+    if (CurrentCBMessage->New == true)
+    {
+#ifdef DEBUG
+  fprintf(stdout,"New CB received\n");
+#endif
+      Message->Channel = CurrentCBMessage->Channel;
+      strcpy(Message->Message,CurrentCBMessage->Message);
+      CurrentCBMessage->New = false;
+      return (GE_NONE);
+    }
+  }
+  return (GE_NONEWCBRECEIVED);
+}
+
+int N6110_MakeCallerGroupFrame(unsigned char *req,GSM_Bitmap Bitmap)
+{
+  int count=0;
+
+  req[count++]=Bitmap.number;
+  req[count++]=strlen(Bitmap.text);
+  memcpy(req+count,Bitmap.text,req[count-1]);
+  count+=req[count-1];
+  req[count++]=Bitmap.ringtone;
+
+  /* Setting for graphic:
+     0x00 - Off
+     0x01 - On
+     0x02 - View Graphics
+     0x03 - Send Graphics
+     0x04 - Send via IR
+     You can even set it higher but Nokia phones (my
+     6110 at least) will not show you the name of this
+     item in menu ;-)) Nokia is really joking here. */
+  if (Bitmap.enabled) req[count++]=0x01;
+                 else req[count++]=0x00;
+
+  req[count++]=(Bitmap.size+4)>>8;
+  req[count++]=(Bitmap.size+4)%0xff;
+  req[count++]=0x00;  /* Future extensions! */
+  req[count++]=Bitmap.width;
+  req[count++]=Bitmap.height;
+  req[count++]=0x01;  /* Just BW */
+  memcpy(req+count,Bitmap.bitmap,Bitmap.size);
+
+  return count+Bitmap.size;
+}
+
+int N6110_MakeOperatorLogoFrame(unsigned char *req,GSM_Bitmap Bitmap)
+{
+  int count=0;
+
+  EncodeNetworkCode(req+count, Bitmap.netcode);
+  count=count+3;
+
+  req[count++]=(Bitmap.size+4)>>8;
+  req[count++]=(Bitmap.size+4)%0xff;
+  req[count++]=0x00;  /* Infofield */
+  req[count++]=Bitmap.width;
+  req[count++]=Bitmap.height;
+  req[count++]=0x01;  /* Just BW */    
+  memcpy(req+count,Bitmap.bitmap,Bitmap.size);
+
+  return count+Bitmap.size;
+}
+
+int N6110_MakeStartupLogoFrame(unsigned char *req,GSM_Bitmap Bitmap)
+{
+  int count=0;
+
+  req[count++]=0x01;
+  req[count++]=Bitmap.height;
+  req[count++]=Bitmap.width;
+  memcpy(req+count,Bitmap.bitmap,Bitmap.size);
+
+  return count+Bitmap.size;
+}
+
+/* Set a bitmap or welcome-note */
+GSM_Error N6110_SetBitmap(GSM_Bitmap *Bitmap) {
+
+  unsigned char req[600] = { N6110_FRAME_HEADER };
+  u16 count=3;
+  u8 textlen;
+  
+  int timeout=50;
+
+  /* Direct uploading variables */
+  GSM_MultiSMSMessage SMS;
+  unsigned char buffer[1000] = {0x0c,0x01};
+  GSM_NetworkInfo NetworkInfo;
+
+  GSM_Error error;
+  /* Uploading with preview */
+  if (Bitmap->number==255 &&
+     (Bitmap->type==GSM_OperatorLogo || Bitmap->type==GSM_CallerLogo)) {
+    GSM_SaveBitmapToSMS(&SMS,Bitmap,false,false);
+    memcpy(buffer+2,SMS.SMS[0].UDH,SMS.SMS[0].UDH[0]+1);
+
+    memcpy(buffer+2+SMS.SMS[0].UDH[0]+1,SMS.SMS[0].MessageText,SMS.SMS[0].Length);
+
+    buffer[2+SMS.SMS[0].UDH[0]+1+SMS.SMS[0].Length]=0x00;
+
+    Protocol->SendMessage(2+SMS.SMS[0].UDH[0]+1+SMS.SMS[0].Length+1, 0x12, buffer);
+
+    GSM->GetNetworkInfo(&NetworkInfo); //need to make something
+    return GE_NONE; //no answer from phone
+  }
+  CurrentSetBitmapError = GE_BUSY;  
+  
+  switch (Bitmap->type) {
+  case GSM_WelcomeNoteText:
+  case GSM_DealerNoteText:
+    req[count++]=0x18;
+    req[count++]=0x01; /* Only one block */
+
+    if (Bitmap->type==GSM_WelcomeNoteText)
+      req[count++]=0x02; /* Welcome text */
+    else
+      req[count++]=0x03; /* Dealer Welcome Note */
+
+    textlen=strlen(Bitmap->text);
+    req[count++]=textlen;
+    memcpy(req+count,Bitmap->text,textlen);
+      
+    count+=textlen;
+
+    Protocol->SendMessage(count, 0x05, req);
+    
+    break;
+
+  case GSM_StartupLogo:
+    if (Bitmap->number==0) {
+
+      /* For 33xx we first set animated logo to default */
+      if (GetModelFeature (FN_STARTUP)==F_STANIM) {
+        error=N6110_SetProfileFeature(0, 0x29, Bitmap->number);
+        if (error!=GE_NONE) return error;
+      }
+
+      req[count++]=0x18;
+      req[count++]=0x01; /* Only one block */
+      count=count+N6110_MakeStartupLogoFrame(req+5,*Bitmap); 
+      Protocol->SendMessage(count, 0x05, req);
+    } else {
+      return N6110_SetProfileFeature(0, 0x29, Bitmap->number);
+    }
+    break;
+
+  case GSM_OperatorLogo:
+    req[count++]=0x30;  /* Store Op Logo */
+    req[count++]=0x01;  /* Location */
+    count=count+N6110_MakeOperatorLogoFrame(req+5,*Bitmap); 
+    Protocol->SendMessage(count, 0x05, req);
+    break;
+
+  case GSM_CallerLogo:
+    req[count++]=0x13;
+    count=count+N6110_MakeCallerGroupFrame(req+4,*Bitmap);
+    Protocol->SendMessage(count, 0x03, req);
+    break;
+
+  case GSM_PictureImage:
+    req[count++]=0x03;
+    req[count++]=Bitmap->number;
+    if (strcmp(Bitmap->Sender,"")) {
+       req[count]=GSM_PackSemiOctetNumber(Bitmap->Sender, req+count+1,true);
+
+       /* Convert number of semioctets to number of chars and add count */
+       textlen=req[count];
+       if (textlen % 2) textlen++;
+       count+=textlen / 2 + 1;
+
+       count++;
+    } else {
+      req[count++]=0x00;
+      req[count++]=0x00;
+    }
+    req[count++]=0x00;
+    req[count++]=strlen(Bitmap->text);
+    memcpy(req+count,Bitmap->text,strlen(Bitmap->text));
+    count+=strlen(Bitmap->text);
+    req[count++]=0x00;
+    req[count++]=Bitmap->width;
+    req[count++]=Bitmap->height;
+    req[count++]=0x01;
+    memcpy(req+count,Bitmap->bitmap,Bitmap->size);
+    Protocol->SendMessage(count+Bitmap->size, 0x47, req);
+    break;
+
+  case GSM_7110OperatorLogo:
+  case GSM_7110StartupLogo:
+  case GSM_6210StartupLogo:
+    return GE_NOTSUPPORTED;
+
+  case GSM_None:
+    return GE_NONE;
+  }
+
+  /* Wait for timeout or other error. */
+  while (timeout != 0 && CurrentSetBitmapError == GE_BUSY ) {
+          
+    if (--timeout == 0)
+      return (GE_TIMEOUT);
+                    
+    usleep (100000);
+  }
+
+  return CurrentSetBitmapError;
+}
+
+/* Get a bitmap from the phone */
+GSM_Error N6110_GetBitmap(GSM_Bitmap *Bitmap) {
+
+  unsigned char req[10] = { N6110_FRAME_HEADER };
+  u8 count=3;
+  
+  int timeout=100;
+  
+  CurrentGetBitmap=Bitmap; 
+  CurrentGetBitmapError = GE_BUSY;  
+  
+  switch (CurrentGetBitmap->type) {
+  case GSM_StartupLogo:
+  case GSM_WelcomeNoteText:
+  case GSM_DealerNoteText:
+    req[count++]=0x16;
+    Protocol->SendMessage(count, 0x05, req);
+    break;
+  case GSM_OperatorLogo:
+    req[count++]=0x33;
+    req[count++]=0x01; /* Location 1 */
+    Protocol->SendMessage(count, 0x05, req);
+    break;
+  case GSM_CallerLogo:
+    req[count++]=0x10;
+    req[count++]=Bitmap->number;
+    Protocol->SendMessage(count, 0x03, req);
+    break;
+  case GSM_PictureImage:
+    req[count++]=0x01;
+    req[count++]=Bitmap->number;
+    Protocol->SendMessage(count, 0x47, req);
+    break;
+  case GSM_7110OperatorLogo:
+  case GSM_7110StartupLogo:
+  case GSM_6210StartupLogo:
+  default:
+    return GE_NOTSUPPORTED;
+  }
+
+  /* Wait for timeout or other error. */
+  while (timeout != 0 && CurrentGetBitmapError == GE_BUSY ) {
+          
+    if (--timeout == 0)
+      return (GE_TIMEOUT);
+                    
+    usleep (100000);
+  }
+
+  CurrentGetBitmap=NULL;
+
+  return CurrentGetBitmapError;
+}
+
+void N6110_ReplySetRingtone(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[3]) {
+
+  /* Set ringtone OK */
+  case 0x37:       
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Ringtone set OK!\n"));
+#endif  
+    CurrentRingtoneError=GE_NONE; 
+    break;      
+
+  /* Set ringtone error */
+  case 0x38:       
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Ringtone setting error !\n"));
+#endif  
+    CurrentRingtoneError=GE_NOTSUPPORTED; 
+    break;      
+  }
+}
+
+GSM_Error N6110_SetRingTone(GSM_Ringtone *ringtone, int *maxlength)
+{
+  
+  char req[FB61_MAX_RINGTONE_FRAME_LENGTH+10] =
+      {N6110_FRAME_HEADER,
+       0x36,
+       0x00,  /* Location */
+       0x00,0x78};
+
+  int size=FB61_MAX_RINGTONE_FRAME_LENGTH;
+  /* Variables for preview uploading */
+  unsigned char buffer[FB61_MAX_RINGTONE_FRAME_LENGTH+50];
+  unsigned char buffer2[20];
+  GSM_NetworkInfo NetworkInfo;
+
+  /* Setting ringtone with preview */
+  if (ringtone->location==255) {
+    buffer[0]=0x0c;
+    buffer[1]=0x01;
+    EncodeUDHHeader(buffer2, GSM_RingtoneUDH);
+    memcpy(buffer+2,buffer2,buffer2[0]+1); //copying UDH
+    *maxlength=GSM_PackRingtone(ringtone, buffer+2+buffer2[0]+1, &size); //packing ringtone
+    Protocol->SendMessage(2+buffer2[0]+1+size, 0x12, buffer); //sending frame
+    GSM->GetNetworkInfo(&NetworkInfo); //need to make something
+    sleep(1);
+    return GE_NONE; //no answer from phone
+  }
+  
+  *maxlength=GSM_PackRingtone(ringtone, req+7, &size);
+
+  req[4]=ringtone->location-1;
+
+  return NULL_SendMessageSequence
+    (50, &CurrentRingtoneError, (size+7), 0x05, req);
+}
+
+void N6110_ReplyGetBinRingtone(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i;
+  
+  switch (MessageBuffer[4]) {
+    case 0x00: /* location supported. We have ringtone */
+
+      /* Binary format used in N6150 */
+      if (MessageBuffer[5]==0x0c && MessageBuffer[6]==0x01 && MessageBuffer[7]==0x2c) {
+#ifdef DEBUG
+        fprintf(stdout,_("Message: ringtone \""));
+#endif      
+
+        /* Copying name */
+        i=8;
+        while (true) {
+#ifdef DEBUG
+          if (MessageBuffer[i]!=0)
+            fprintf(stdout,_("%c"),MessageBuffer[i]);
+#endif
+          CurrentGetBinRingtone->name[i-8]=MessageBuffer[i];
+          if (MessageBuffer[i]==0) break;
+          i++;
+        }
+
+#ifdef DEBUG    
+        fprintf(stdout,_("\" received from location %i\n"),MessageBuffer[3]+1);
+#endif
+      
+        /* Looking for end */
+        i=0;
+       while (true) {
+         if (MessageBuffer[i]==0x07 && MessageBuffer[i+1]==0x0b) {
+           i=i+2;break;
+         }
+         if (MessageBuffer[i]==0x0e && MessageBuffer[i+1]==0x0b) {
+           i=i+2;break;
+         }
+         i++;
+          if (i==MessageLength) break;
+       }
+         
+        /* Copying frame */
+        memcpy(CurrentGetBinRingtone->frame,MessageBuffer+3,i-3);
+        CurrentGetBinRingtone->length=i-3;
+      
+        CurrentBinRingtoneError=GE_NONE;
+        break;
+      }
+         
+      /* Binary format used in N3210 */
+      if (MessageBuffer[5]==0x10 && MessageBuffer[6]==0x01 && MessageBuffer[7]==0x2c) {      
+
+#ifdef DEBUG
+        fprintf(stdout,_("Message: ringtone \""));
+#endif      
+
+        /* Copying name */
+        i=8;
+        while (true) {
+#ifdef DEBUG
+          if (MessageBuffer[i]!=0)
+            fprintf(stdout,_("%c"),MessageBuffer[i]);
+#endif
+          CurrentGetBinRingtone->name[i-8]=MessageBuffer[i];
+          if (MessageBuffer[i]==0) break;
+          i++;
+        }
+
+#ifdef DEBUG    
+        fprintf(stdout,_("\" received from location %i\n"),MessageBuffer[3]+1);
+#endif
+
+        /* Here changes to get full compatibility with binary format used in N6150 */
+        MessageBuffer[3]=0;
+        MessageBuffer[4]=0;
+        MessageBuffer[5]=0x0c;
+        MessageBuffer[6]=0x01;
+        MessageBuffer[7]=0x2c;
+
+        /* Looking for end */
+        i=0;
+        while (true) {
+          if (MessageBuffer[i]==0x07 && MessageBuffer[i+1]==0x0b) {
+           i=i+2;break;
+         }
+         if (MessageBuffer[i]==0x0e && MessageBuffer[i+1]==0x0b) {
+           i=i+2;break;
+         }
+         i++;
+         if (i==MessageLength) break;
+        }
+         
+       /* Copying frame */
+        memcpy(CurrentGetBinRingtone->frame,MessageBuffer+3,i-3);
+
+        CurrentGetBinRingtone->length=i-3;
+           
+        CurrentBinRingtoneError=GE_NONE;         
+       break;
+      }
+
+      /* Copying frame */
+      memcpy(CurrentGetBinRingtone->frame,MessageBuffer,MessageLength);
+
+      CurrentGetBinRingtone->length=MessageLength;
+
+#ifdef DEBUG    
+      fprintf(stdout,_("Message: unknown binary format for ringtone received from location %i\n"),MessageBuffer[3]+1);
+#endif
+      CurrentBinRingtoneError=GE_UNKNOWNMODEL;
+      break;
+
+    default:
+
+#ifdef DEBUG
+      fprintf(stdout,_("Message: Phone doesn't support downloaded ringtones at location %i\n"),MessageBuffer[3]+1);
+#endif
+
+      CurrentBinRingtoneError=GE_INVALIDRINGLOCATION;  
+  }
+}
+
+GSM_Error N6110_GetBinRingTone(GSM_BinRingtone *ringtone)
+{
+  unsigned char req[] = { 0x00,0x01,0x9e,
+                          0x00 }; //location
+
+  GSM_Error error;
+  
+  CurrentGetBinRingtone=ringtone;
+  
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+
+  req[3]=ringtone->location-1;
+  
+  return NULL_SendMessageSequence
+    (50, &CurrentBinRingtoneError, 4, 0x40, req);
+}
+
+void N6110_ReplySetBinRingtone(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch (MessageBuffer[4]) {
+    case 0x00: /* location supported. We set ringtone */
+#ifdef DEBUG
+      fprintf(stdout,_("Message: downloaded ringtone set at location %i\n"),MessageBuffer[3]+1);
+#endif
+      CurrentBinRingtoneError=GE_NONE;
+      break;
+
+    default:
+#ifdef DEBUG
+      fprintf(stdout,_("Message: Phone doesn't support downloaded ringtones at location %i\n"),MessageBuffer[3]+1);
+#endif
+      CurrentBinRingtoneError=GE_NOTSUPPORTED;   
+      break;
+  }
+}
+
+GSM_Error N6110_SetBinRingTone(GSM_BinRingtone *ringtone)
+{
+  unsigned char req[1000] = { 0x00,0x01,0xa0};
+
+  GSM_Error error;
+
+  GSM_BinRingtone ring;
+
+  /* Must be sure, that can upload ringtone to this phone */
+  ring.location=ringtone->location;
+  error=N6110_GetBinRingTone(&ring);
+  if (error!=GE_NONE) return error;
+    
+  error=N6110_EnableExtendedCommands(0x01);
+  if (error!=GE_NONE) return error;
+  
+  memcpy(req+3,ringtone->frame,ringtone->length);
+
+  req[3]=ringtone->location-1;
+  
+  return NULL_SendMessageSequence
+    (50, &CurrentBinRingtoneError, ringtone->length+3, 0x40, req);
+}
+
+GSM_Error N6110_Reset(unsigned char type)
+{  
+  return N6110_EnableExtendedCommands(type);
+}
+
+void N6110_Dispatch0x01Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int tmp, count;
+         
+  switch (MessageBuffer[3]) {
+
+  /* Unknown message - it has been seen after the 0x07 message (call
+     answered). Probably it has similar meaning. If you can solve
+     this - just mail me. Pavel Janík ml.
+
+     The message looks like this:
+
+     Msg Destination: PC
+     Msg Source: Phone
+     Msg Type: 01
+     Msg Unknown: 00
+     Msg Len: 0e
+
+     Phone: [01 ][08 ][00 ] is the header of the frame
+
+     [03 ] is the call message subtype
+
+     [05 ] is the call sequence number
+
+     [05 ] unknown 
+
+     [00 ][01 ][03 ][02 ][91][00] are unknown but has been
+     seen in the Incoming call message (just after the
+     caller's name from the phonebook). But never change
+     between phone calls :-(
+  */
+
+  /* This may mean sequence number of 'just made' call - CK */
+  case 0x02:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call message, type 0x02:"));
+    fprintf(stdout, _("   Exact meaning not known yet, sorry :-(\n"));
+#endif /* DEBUG */
+
+    break;
+
+  /* Possibly call OK */
+  /* JD: I think that this means "call in progress" (incomming or outgoing) */
+  case 0x03:
+    
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call message, type 0x03:"));
+    fprintf(stdout, _("   Sequence nr. of the call: %d\n"), MessageBuffer[4]);
+    fprintf(stdout, _("   Exact meaning not known yet, sorry :-(\n"));
+#endif /* DEBUG */
+    
+    CurrentCallSequenceNumber=MessageBuffer[4];
+    CurrentIncomingCall[0]='D';
+    if (CurrentCallPassup) CurrentCallPassup('D');
+
+    break;
+
+  /* Remote end has gone away before you answer the call.  Probably your
+     mother-in-law or banker (which is worse?) ... */
+  case 0x04:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Remote end hang up.\n"));
+    fprintf(stdout, _("   Sequence nr. of the call: %d, error: %i"), MessageBuffer[4],MessageBuffer[6]);
+
+    switch (MessageBuffer[6]) {
+      case 28: fprintf(stdout,_(" (info \"Invalid phone number\")"));break;
+      case 34: fprintf(stdout,_(" (info \"Network busy\")"));break;
+      case 42: fprintf(stdout,_(" (info \"Network busy\")"));break;
+      case 47: fprintf(stdout,_(" (info \"Error in connection\")"));break;
+      case 50: fprintf(stdout,_(" (info \"Check operator services\")"));break;       case 76: fprintf(stdout,_(" (info \"Check operator services\")"));break;
+      case 111: fprintf(stdout,_(" (info \"Error in connection\")"));break;
+    }
+      
+    fprintf(stdout,_("\n   For more details with errors see netmonitor manual (test 39) on www.marcin-wiacek.topnet.pl"));
+    fprintf(stdout,_("\n   If know their meaning, GSM specs decribing them, contact with me on marcin-wiacek@topnet.pl. THX\n"));
+#endif /* DEBUG */
+
+    CurrentIncomingCall[0] = ' ';
+    if (CurrentCallPassup) CurrentCallPassup(' ');
+
+    break;
+
+  /* Incoming call alert */
+  case 0x05:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Incoming call alert:\n"));
+
+    /* We can have more then one call ringing - we can distinguish between
+       them */
+
+    fprintf(stdout, _("   Sequence nr. of the call: %d\n"), MessageBuffer[4]);
+    fprintf(stdout, _("   Number: "));
+
+    count=MessageBuffer[6];
+
+    for (tmp=0; tmp <count; tmp++)
+      fprintf(stdout, "%c", MessageBuffer[7+tmp]);
+
+    fprintf(stdout, "\n");
+
+    fprintf(stdout, _("   Name: "));
+
+    for (tmp=0; tmp <MessageBuffer[7+count]; tmp++)
+      fprintf(stdout, "%c", MessageBuffer[8+count+tmp]);
+
+    fprintf(stdout, "\n");
+#endif /* DEBUG */
+
+    count=MessageBuffer[6];
+
+    CurrentIncomingCall[0] = 0;
+    for (tmp=0; tmp <count; tmp++)
+      sprintf(CurrentIncomingCall, "%s%c", CurrentIncomingCall, MessageBuffer[7+tmp]);
+
+    break;
+
+  /* Call answered. Probably your girlfriend...*/
+  case 0x07:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call answered.\n"));
+    fprintf(stdout, _("   Sequence nr. of the call: %d\n"), MessageBuffer[4]);
+#endif /* DEBUG */
+
+    break;
+
+  /* Call ended. Girlfriend is girlfriend, but time is money :-) */
+  case 0x09:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call ended by your phone.\n"));
+    fprintf(stdout, _("   Sequence nr. of the call: %d\n"), MessageBuffer[4]);
+#endif /* DEBUG */
+
+    break;
+
+  /* This message has been seen with the message of subtype 0x09
+     after I hang the call.
+
+  Msg Destination: PC
+  Msg Source: Phone
+  Msg Type: 01 
+  Msg Unknown: 00
+  Msg Len: 08
+  Phone: [01 ][08 ][00 ][0a ][04 ][87 ][01 ][42B][1a ][c2 ]
+
+  What is the meaning of 87? Can you spell some magic light into
+  this issue?
+
+  */
+
+  /* Probably means call over - CK */
+  case 0x0a:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Call message, type 0x0a:"));
+    fprintf(stdout, _("   Sequence nr. of the call: %d\n"), MessageBuffer[4]);
+    fprintf(stdout, _("   Exact meaning not known yet, sorry :-(\n"));
+#endif /* DEBUG */
+
+    CurrentIncomingCall[0] = ' ';
+    if (CurrentCallPassup) CurrentCallPassup(' ');
+
+    break;
+
+  case 0x40:
+
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Answer for send DTMF or dial voice command\n"));
+#endif
+
+    if (CurrentSendDTMFError!=GE_NONE) CurrentSendDTMFError=GE_NONE;
+
+    if (CurrentDialVoiceError!=GE_NONE) CurrentDialVoiceError=GE_NONE;
+
+    break;
+     
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Unknown message of type 0x01\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);
+
+    break;     /* Visual C Don't like empty cases */
+  }
+}
+
+void N6110_Dispatch0x03Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int tmp, count;
+    
+  switch (MessageBuffer[3]) {
+
+  case 0x04:
+
+    /* AFAIK, this frame isn't used anywhere - it's rather for testing :-) */
+    /* If you want see, if it works with your phone make something like that: */
+
+    /* unsigned char connect5[] = {N6110_FRAME_HEADER, 0x03}; */
+    /* Protocol->SendMessage(4, 0x04, connect5); */
+
+    /*                                        Marcin-Wiacek@TopNet.PL */
+    
+#ifdef WIN32
+    sprintf(Current_IMEI, "%s", MessageBuffer+5);
+    sprintf(Current_Model, "%s", MessageBuffer+21);
+    sprintf(Current_Revision, "SW%s, HW%s", MessageBuffer+41, MessageBuffer+35);
+#else
+    snprintf(Current_IMEI, GSM_MAX_IMEI_LENGTH, "%s", MessageBuffer+5);
+    snprintf(Current_Model, GSM_MAX_MODEL_LENGTH, "%s", MessageBuffer+21);
+    snprintf(Current_Revision, GSM_MAX_REVISION_LENGTH, "SW%s, HW%s", MessageBuffer+41, MessageBuffer+35);
+#endif
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Mobile phone identification received:\n"));
+    fprintf(stdout, _("   IMEI: %s\n"), Current_IMEI);
+    fprintf(stdout, _("   Model: %s\n"), Current_Model);
+    fprintf(stdout, _("   Production Code: %s\n"), MessageBuffer+27);
+    fprintf(stdout, _("   HW: %s\n"), MessageBuffer+35);
+    fprintf(stdout, _("   Firmware: %s\n"), MessageBuffer+41);
+#endif /* DEBUG */
+
+    break;
+
+  /* Get group data */    
+  /* [ID],[name_len],[name].,[ringtone],[graphicon],[lenhi],[lenlo],[bitmap] */
+  case 0x11:   
+    if (CurrentGetBitmap!=NULL) {
+      if (CurrentGetBitmap->number==MessageBuffer[4]) {
+        count=MessageBuffer[5];
+        memcpy(CurrentGetBitmap->text,MessageBuffer+6,count);
+        CurrentGetBitmap->text[count]=0;
+
+#ifdef DEBUG   
+       fprintf(stdout, _("Message: Caller group datas\n"));
+       fprintf(stdout, _("Caller group name: %s\n"),CurrentGetBitmap->text);
+#endif /* DEBUG */
+
+       count+=6;
+
+       CurrentGetBitmap->ringtone=MessageBuffer[count++];
+#ifdef DEBUG   
+        fprintf(stdout, _("Caller group ringtone ID: %i"),CurrentGetBitmap->ringtone);
+        if (CurrentGetBitmap->ringtone==16) fprintf(stdout,_(" (default)"));
+       fprintf(stdout,_("\n"));
+#endif /* DEBUG */
+
+       CurrentGetBitmap->enabled=(MessageBuffer[count++]==1);
+#ifdef DEBUG   
+       fprintf(stdout, _("Caller group logo "));
+       if (CurrentGetBitmap->enabled)
+         fprintf(stdout, _("enabled \n"));
+       else
+         fprintf(stdout, _("disabled \n"));
+#endif /* DEBUG */     
+
+       CurrentGetBitmap->size=MessageBuffer[count++]<<8;
+       CurrentGetBitmap->size+=MessageBuffer[count++];
+#ifdef DEBUG   
+       fprintf(stdout, _("Bitmap size=%i\n"),CurrentGetBitmap->size);
+#endif /* DEBUG */
+
+       count++;
+       CurrentGetBitmap->width=MessageBuffer[count++];
+        CurrentGetBitmap->height=MessageBuffer[count++];
+       count++;
+       tmp=GSM_GetBitmapSize(CurrentGetBitmap);
+       if (CurrentGetBitmap->size>tmp) CurrentGetBitmap->size=tmp;
+       memcpy(CurrentGetBitmap->bitmap,MessageBuffer+count,CurrentGetBitmap->size);
+       CurrentGetBitmapError=GE_NONE;
+      } else {
+#ifdef DEBUG   
+       fprintf(stdout, _("Message: Caller group datas received, but group number does not match (%i is not %i)\n"),MessageBuffer[4],CurrentGetBitmap->number);
+#endif
+      }
+    } else {
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Caller group data received but not requested!\n"));
+#endif
+    }
+    break;
+
+  /* Get group data error */
+  case 0x12:   
+      
+    CurrentGetBitmapError=GE_UNKNOWN;   
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Error attempting to get caller group data.\n"));
+#endif   
+    break;
+
+  /* Set group data OK */      
+  case 0x14:   
+      
+    CurrentSetBitmapError=GE_NONE;      
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Caller group data set correctly.\n"));
+#endif
+    break;
+
+  /* Set group data error */
+  case 0x15:   
+      
+    CurrentSetBitmapError=GE_UNKNOWN;      
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Error attempting to set caller group data\n"));
+#endif
+    break;  
+  
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Unknown message of type 0x03\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);
+
+    break;     /* Visual C Don't like empty cases */
+  }
+}
+
+void N6110_Dispatch0x05Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int tmp, count, length;
+  bool issupported;
+
+#ifdef DEBUG
+  int i;
+#endif
+
+  switch (MessageBuffer[3]) {
+
+  /* Startup Logo */
+  case 0x17:  
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Startup Logo, welcome note and dealer welcome note received.\n"));
+#endif
+
+    if (CurrentGetBitmap!=NULL) {
+       
+      issupported=false;
+       
+      count=5;
+       
+      for (tmp=0;tmp<MessageBuffer[4];tmp++){
+        switch (MessageBuffer[count++]) {
+        case 0x01:
+          if (CurrentGetBitmap->type==GSM_StartupLogo) {
+            CurrentGetBitmap->height=MessageBuffer[count++];
+           CurrentGetBitmap->width=MessageBuffer[count++];
+           CurrentGetBitmap->size=GSM_GetBitmapSize(CurrentGetBitmap);
+           length=CurrentGetBitmap->size;
+           memcpy(CurrentGetBitmap->bitmap,MessageBuffer+count,length);
+          } else {
+            //bitmap size
+           length=MessageBuffer[count++];
+           length=length*MessageBuffer[count++]/8;
+         }
+         count+=length;
+#ifdef DEBUG
+         fprintf(stdout, _("Startup logo supported - "));
+         if (length!=0) { fprintf(stdout, _("currently set\n"));   }
+                    else { fprintf(stdout, _("currently empty\n")); }
+#endif
+          if (CurrentGetBitmap->type==GSM_StartupLogo) issupported=true;
+         break;
+       case 0x02:
+         length=MessageBuffer[count];
+         if (CurrentGetBitmap->type==GSM_WelcomeNoteText) {
+            memcpy(CurrentGetBitmap->text,MessageBuffer+count+1,length);
+           CurrentGetBitmap->text[length]=0;
+         }
+#ifdef DEBUG
+          fprintf(stdout, _("Startup Text supported - "));
+          if (length!=0)
+         {
+           fprintf(stdout, _("currently set to \""));
+           for (i=0;i<length;i++) fprintf(stdout, _("%c"),MessageBuffer[count+1+i]);
+           fprintf(stdout, _("\"\n"));
+         } else {
+            fprintf(stdout, _("currently empty\n"));
+         }
+#endif
+         count+=length+1;
+          if (CurrentGetBitmap->type==GSM_WelcomeNoteText) issupported=true;
+         break;
+       case 0x03:
+         length=MessageBuffer[count];
+         if (CurrentGetBitmap->type==GSM_DealerNoteText) {
+            memcpy(CurrentGetBitmap->text,MessageBuffer+count+1,length);
+           CurrentGetBitmap->text[length]=0;
+         }
+#ifdef DEBUG
+         fprintf(stdout, _("Dealer Welcome supported - "));
+         if (length!=0)
+         {
+           fprintf(stdout, _("currently set to \""));
+           for (i=0;i<length;i++) fprintf(stdout, _("%c"),MessageBuffer[count+1+i]);
+           fprintf(stdout, _("\"\n"));
+         } else {
+            fprintf(stdout, _("currently empty\n"));
+         }
+#endif
+         count+=length+1;
+          if (CurrentGetBitmap->type==GSM_DealerNoteText) issupported=true;
+         break;
+        }
+      }
+      if (issupported) CurrentGetBitmapError=GE_NONE;
+                  else CurrentGetBitmapError=GE_NOTSUPPORTED;
+    } else {
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Startup logo received but not requested!\n"));
+#endif
+    }
+    break;
+
+  /* Set startup OK */
+  case 0x19:   
+    
+    CurrentSetBitmapError=GE_NONE;    
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Startup logo, welcome note or dealer welcome note correctly set.\n"));
+#endif  
+    break;      
+
+  /* Set Operator Logo OK */
+  case 0x31:   
+      
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Operator logo correctly set.\n"));
+#endif  
+
+    CurrentSetBitmapError=GE_NONE;      
+    break;
+
+  /* Set Operator Logo Error */      
+  case 0x32:  
+      
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Error setting operator logo!\n"));
+#endif
+
+    CurrentSetBitmapError=GE_UNKNOWN;        
+    break;
+
+  /* Operator Logo */
+  /* [location],[netcode x 3],[lenhi],[lenlo],[bitmap] */ 
+  case 0x34:
+    if (CurrentGetBitmap!=NULL) {
+
+      count=5;  /* Location ignored. */
+
+      DecodeNetworkCode(MessageBuffer+count, CurrentGetBitmap->netcode);
+      count=count+3;
+
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Operator Logo for %s (%s) network received.\n"),
+                          CurrentGetBitmap->netcode,
+                          GSM_GetNetworkName(CurrentGetBitmap->netcode));
+#endif  
+
+      CurrentGetBitmap->size=MessageBuffer[count++]<<8;
+      CurrentGetBitmap->size+=MessageBuffer[count++];
+      count++;
+      CurrentGetBitmap->width=MessageBuffer[count++];
+      CurrentGetBitmap->height=MessageBuffer[count++];
+      count++;
+      tmp=GSM_GetBitmapSize(CurrentGetBitmap);
+      if (CurrentGetBitmap->size>tmp) CurrentGetBitmap->size=tmp;
+      memcpy(CurrentGetBitmap->bitmap,MessageBuffer+count,CurrentGetBitmap->size);
+      CurrentGetBitmapError=GE_NONE;
+    } else {
+#ifdef DEBUG
+      fprintf(stdout, _("Message: Operator logo received but not requested!\n"));
+#endif
+    }
+      
+    break;
+
+  /* Get op logo error */      
+  case 0x35:
+     
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Error getting operator logo!\n"));
+#endif  
+    CurrentGetBitmapError=GE_UNKNOWN; 
+    break;
+
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Unknown message of type 0x05\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);
+
+    break;
+  }
+}
+
+void N6110_Dispatch0x06Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int tmp;
+  unsigned char output[160];
+
+#ifdef DEBUG
+  int i;
+#endif
+    
+  switch (MessageBuffer[3]) {
+
+  case 0x05:
+
+    /* MessageBuffer[3] = 0x05
+       MessageBuffer[4] = 0x00
+       MessageBuffer[5] = 0x0f
+       MessageBuffer[6] = 0x03
+       MessageBuffer[7] = length of packed message
+
+       This is all I have seen - Gerry Anderson */
+
+    tmp=GSM_UnpackEightBitsToSeven(0, 82, 82, MessageBuffer+8, output);
+
+#ifdef DEBUG
+
+    fprintf(stdout, _("Message from Network operator: "));
+
+    for (i=0; i<tmp; i++)
+       fprintf(stdout, "%c", DecodeWithDefaultAlphabet(output[i]));
+
+    fprintf(stdout, "\n");
+
+#endif /* DEBUG */
+
+    break;
+
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Unknown message of type 0x06\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);
+
+    break;
+  }
+}
+
+void N6110_Dispatch0x09Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+    
+  switch (MessageBuffer[3]) {
+    
+  case 0x80:    
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SIM card login\n"));
+#endif
+    break;
+
+  case 0x81:    
+#ifdef DEBUG
+    fprintf(stdout, _("Message: SIM card logout\n"));
+#endif
+    break;
+      
+  default:
+#ifdef DEBUG
+    fprintf(stdout, _("Unknown message of type 0x09.\n"));      
+#endif
+    AppendLogText("Unknown msg\n",false);
+    break;
+  }
+}
+
+void N6110_Dispatch0x13Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  switch(MessageBuffer[3]) {
+    
+  case 0x6a:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Calendar Alarm active\n"));
+    fprintf(stdout, _("   Item number: %d\n"), MessageBuffer[4]);
+#endif /* DEBUG */
+
+  default:
+#ifdef DEBUG
+    fprintf(stdout, _("Unknown message of type 0x13.\n"));      
+#endif
+    AppendLogText("Unknown msg\n",false);
+    break;
+  }
+}
+
+void N6110_Dispatch0x40Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int i;
+  
+  switch(MessageBuffer[2]) {
+
+  case 0x02:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: ACK for simlock opening part 1\n"));
+#endif /* DEBUG */
+    
+    CurrentMagicError=GE_NONE;
+    break;
+    
+  case 0x7c:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Answer for call commands.\n"));
+#endif
+    
+    CurrentDialVoiceError=GE_NONE;      
+    break;
+      
+  case 0x81:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: ACK for simlock opening part 2\n"));
+#endif /* DEBUG */
+    
+    CurrentMagicError=GE_NONE;
+    break;
+
+  case 0x82:
+
+#ifdef DEBUG
+      fprintf(stdout, _("Message: ACK for simlock closing\n"));
+#endif /* DEBUG */
+    
+    CurrentMagicError=GE_NONE;
+    break;
+
+  case 0xd4:
+
+    switch (MessageBuffer[5]) {
+      case 0xa0:
+#ifdef DEBUG
+        fprintf(stdout,_("Message: EEPROM contest received\n"));
+#endif
+
+        if (MessageBuffer[8]!=0x00) {
+          for (i=9;i<MessageLength;i++) {
+            fprintf(stdout,_("%c"), MessageBuffer[i]);
+        }
+
+        CurrentMagicError=GE_NONE;
+      }
+      
+      break;
+    }
+      
+#ifdef DEBUG
+    fprintf(stdout, _("Unknown message of type 0x40.\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);      
+    break;
+
+  case 0xcf:
+
+    N6110_DisplayTestsInfo(MessageBuffer);
+    break;
+      
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Unknown message of type 0x40.\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);
+    break;     /* Visual C Don't like empty cases */
+  }
+}
+
+void N6110_Dispatch0x47Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  int count;
+  
+  switch(MessageBuffer[3]) {
+    
+  case 0x02:
+
+    count=5;
+    
+    if (MessageBuffer[5]!=0) {
+      strcpy(CurrentGetBitmap->Sender,GSM_UnpackSemiOctetNumber(MessageBuffer+5,true));
+
+      while (MessageBuffer[count]!=0) {
+        count++;
+      }
+
+      count++;
+    } else {
+      strcpy(CurrentGetBitmap->Sender,"\0");
+
+      count+=3;
+    }
+
+    memcpy(CurrentGetBitmap->text,MessageBuffer+count+1,MessageBuffer[count]);
+    CurrentGetBitmap->text[MessageBuffer[count]]=0;
+
+    if (MessageBuffer[count]!=0)
+      count+=MessageBuffer[count];
+
+    count++;
+
+#ifdef DEBUG
+    fprintf(stdout,_("Picture Image received, text \"%s\", sender %s\n"),CurrentGetBitmap->text,CurrentGetBitmap->Sender);
+#endif
+
+    CurrentGetBitmap->width=MessageBuffer[count+1];
+    CurrentGetBitmap->height=MessageBuffer[count+2]; 
+    CurrentGetBitmap->size=GSM_GetBitmapSize(CurrentGetBitmap);
+      
+    memcpy(CurrentGetBitmap->bitmap,MessageBuffer+count+4,CurrentGetBitmap->size);
+      
+    CurrentGetBitmapError=GE_NONE;
+    break;
+
+  case 0x04:
+
+#ifdef DEBUG
+    fprintf(stdout,_("Getting or setting Picture Image - OK\n"));
+#endif
+    CurrentSetBitmapError=GE_NONE;
+    CurrentGetBitmapError=GE_NONE;
+    break;     
+
+  case 0x05:
+
+#ifdef DEBUG
+    fprintf(stdout,_("Setting Picture Image - invalid location or other error\n"));
+#endif
+    CurrentSetBitmapError=GE_UNKNOWN;
+    break;     
+
+  case 0x06:
+
+#ifdef DEBUG
+    fprintf(stdout,_("Getting Picture Image - invalid location or other error\n"));
+#endif
+    CurrentGetBitmapError=GE_UNKNOWN;
+    break;     
+
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Unknown message of type 0x47.\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg\n",false);
+    break;     /* Visual C Don't like empty cases */
+  }
+}
+
+void N6110_DispatchACKMessage(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+  char buffer[50];
+  
+  sprintf(buffer,"Received ACK %02x %02x\n",MessageBuffer[0],MessageBuffer[1]);
+  AppendLog(buffer,strlen(buffer),false);
+
+#ifdef DEBUG
+  fprintf(stdout, _("[Received Ack of type %02x, seq: %2x]\n"), MessageBuffer[0],
+                                                                MessageBuffer[1]);
+#endif /* DEBUG */
+  
+  CurrentLinkOK = true;
+}
+
+void N6110_Dispatch0xD0Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+   
+#ifdef DEBUG
+  fprintf(stdout, _("Message: The phone is powered on - seq 1.\n"));
+#endif /* DEBUG */
+
+}
+
+/* This function is used for parsing the RLP frame into fields. */
+void N6110_RX_HandleRLPMessage(u8 *MessageBuffer)
+{
+
+  RLP_F96Frame frame;
+  int count;
+  int valid = true;
+
+  /* We do not need RLP frame parsing to be done when we do not have callback
+     specified. */
+  if (CurrentRLP_RXCallback == NULL)
+    exit;
+    
+  /* Anybody know the official meaning of the first two bytes?
+     Nokia 6150 sends junk frames starting D9 01, and real frames starting
+     D9 00. We'd drop the junk frames anyway because the FCS is bad, but
+     it's tidier to do it here. We still need to call the callback function
+     to give it a chance to handle timeouts and/or transmit a frame */
+  if (MessageBuffer[0] == 0xd9 && MessageBuffer[1] == 0x01)
+    valid = false;
+
+  /* Nokia uses 240 bit frame size of RLP frames as per GSM 04.22
+     specification, so Header consists of 16 bits (2 bytes). See section 4.1
+     of the specification. */
+    
+  frame.Header[0] = MessageBuffer[2];
+  frame.Header[1] = MessageBuffer[3];
+
+  /* Next 200 bits (25 bytes) contain the Information. We store the
+     information in the Data array. */
+
+  for (count = 0; count < 25; count ++)
+    frame.Data[count] = MessageBuffer[4 + count];
+
+  /* The last 24 bits (3 bytes) contain FCS. */
+
+  frame.FCS[0] = MessageBuffer[29];
+  frame.FCS[1] = MessageBuffer[30];
+  frame.FCS[2] = MessageBuffer[31];
+
+  /* Here we pass the frame down in the input stream. */
+  CurrentRLP_RXCallback(valid ? &frame : NULL);
+}
+
+void N6110_Dispatch0xF4Message(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+#ifdef DEBUG
+  fprintf(stdout, _("Message: The phone is powered on - seq 2.\n"));
+#endif /* DEBUG */
+
+}
+
+void N6110_ReplyIncomingSMS(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  GSM_SMSMessage NullSMS;
+
+  switch (MessageBuffer[6]) {
+
+    case 0x00: NullSMS.Type = GST_SMS; NullSMS.folder = GST_INBOX; break;
+    case 0x01: NullSMS.Type = GST_DR;  NullSMS.folder = GST_INBOX; break;
+
+    /* Is it possible ? */
+    case 0x02: NullSMS.Type = GST_SMS; NullSMS.folder = GST_OUTBOX; break;      
+    default:   NullSMS.Type = GST_UN;                               break;
+  }
+
+#ifdef DEBUG
+  if (NullSMS.Type == GST_DR)
+    fprintf(stdout, _("Message: SMS Message (Report) Received\n"));
+  else 
+    fprintf(stdout, _("Message: SMS Message Received\n"));  
+#endif /* DEBUG */
+
+  GSM_DecodeNokiaSMSFrame(&NullSMS, MessageBuffer+7, MessageLength-7);
+
+#ifdef DEBUG
+  fprintf(stdout, _("\n"));      
+#endif /* DEBUG */
+}
+
+void N6110_DispatchMessage(u16 MessageLength, u8 *MessageBuffer, u8 MessageType) {
+
+  bool unknown=false;
+
+  /* Switch on the basis of the message type byte */
+  switch (MessageType) {
+         
+  /* Call information */
+  case 0x01:
+
+    N6110_Dispatch0x01Message(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  /* SMS handling */
+  case 0x02:
+    switch (MessageBuffer[3]) {
+      case 0x02:
+      case 0x03:N6110_ReplySendSMSMessage(MessageLength,MessageBuffer,MessageType);break;
+      case 0x10:N6110_ReplyIncomingSMS(MessageLength,MessageBuffer,MessageType);break;
+      case 0x21:N6110_ReplySetCellBroadcast(MessageLength, MessageBuffer, MessageType);break;
+      case 0x23:N6110_ReplyReadCellBroadcast(MessageLength, MessageBuffer, MessageType);break;
+      case 0x31:N6110_ReplySetSMSCenter(MessageLength,MessageBuffer,MessageType);break;
+      case 0x34:
+      case 0x35:N6110_ReplyGetSMSCenter(MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* Phonebook handling */
+  case 0x03:
+    switch (MessageBuffer[3]) {
+      case 0x02:
+      case 0x03:N6110_ReplyGetMemoryLocation(MessageLength,MessageBuffer,MessageType);break;
+      case 0x05:
+      case 0x06:N6110_ReplyWritePhonebookLocation(MessageLength,MessageBuffer,MessageType);break;
+      case 0x08:
+      case 0x09:N6110_ReplyGetMemoryStatus(MessageLength,MessageBuffer,MessageType);break;
+      case 0x17:
+      case 0x18:N6110_ReplyGetSpeedDial(MessageLength,MessageBuffer,MessageType);break;
+      case 0x1a:
+      case 0x1b:N6110_ReplySetSpeedDial(MessageLength,MessageBuffer,MessageType);break;
+      default  :N6110_Dispatch0x03Message(MessageLength,MessageBuffer,MessageType);break;
+    }
+    break;
+
+  /* Phone status */    
+  case 0x04:
+    switch (MessageBuffer[3]) {
+      case 0x02:N6110_ReplyRFBatteryLevel(MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+      
+  /* Startup Logo, Operator Logo and Profiles. */
+  case 0x05:
+    switch (MessageBuffer[3]) {
+      case 0x11:N6110_ReplySetProfile    (MessageLength,MessageBuffer,MessageType);break;
+      case 0x14:N6110_ReplyGetProfile    (MessageLength,MessageBuffer,MessageType);break;
+      case 0x1b:N6110_ReplyGetProfile    (MessageLength,MessageBuffer,MessageType);break;
+      case 0x1d:N6110_ReplySetProfile    (MessageLength,MessageBuffer,MessageType);break;
+      case 0x37:N6110_ReplySetRingtone   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x38:N6110_ReplySetRingtone   (MessageLength,MessageBuffer,MessageType);break;
+      default  :N6110_Dispatch0x05Message(MessageLength,MessageBuffer,MessageType);break;
+    }
+    break;
+
+  /* Network Operator Message to handset -> Gerry Anderson & prepaid info */
+  /* Call diverts */
+  case 0x06:
+    switch (MessageBuffer[3]) {
+      case 0x02:
+      case 0x03:N6110_ReplyCallDivert    (MessageLength,MessageBuffer,MessageType);break;
+      default  :N6110_Dispatch0x06Message(MessageLength,MessageBuffer,MessageType);break;
+    }
+    break;
+
+  /* Security code requests */
+  case 0x08:
+    switch (MessageBuffer[3]) {
+      case 0x08:N6110_ReplyGetSecurityCodeStatus(MessageLength,MessageBuffer,MessageType);break;
+      case 0x0b:N6110_ReplyEnterSecurityCode    (MessageLength,MessageBuffer,MessageType);break;
+      default  :N6110_ReplyEnterSecurityCode    (MessageLength,MessageBuffer,MessageType);break;
+    }
+    break;
+
+  /* SIM login */
+  case 0x09:
+
+    N6110_Dispatch0x09Message(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  /* Network info */
+  case 0x0a:
+    switch (MessageBuffer[3]) {
+      case 0x71:N6110_ReplyGetNetworkInfo(MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* Simulating key pressing */
+  case 0x0c:
+    switch (MessageBuffer[3]) {
+      case 0x43:N6110_ReplyPressKey(MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* Display */
+  case 0x0d:
+    switch (MessageBuffer[3]) {
+      case 0x50:N6110_ReplyDisplayOutput   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x52:N6110_ReplyGetDisplayStatus(MessageLength,MessageBuffer,MessageType);break;
+      case 0x54:N6110_ReplyDisplayOutput   (MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* Phone Clock and Alarm */
+  case 0x11:
+    switch (MessageBuffer[3]) {
+      case 0x61:N6110_ReplySetDateTime(MessageLength,MessageBuffer,MessageType);break;
+      case 0x63:N6110_ReplyGetDateTime(MessageLength,MessageBuffer,MessageType);break;
+      case 0x6c:N6110_ReplySetAlarm   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x6e:N6110_ReplyGetAlarm   (MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* Calendar notes handling */
+  case 0x13:
+    switch (MessageBuffer[3]) {
+      case 0x65:N6110_ReplyWriteCalendarNote (MessageLength,MessageBuffer,MessageType);break;
+      case 0x67:N6110_ReplyGetCalendarNote   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x69:N6110_ReplyDeleteCalendarNote(MessageLength,MessageBuffer,MessageType);break;
+      default  :N6110_Dispatch0x13Message    (MessageLength,MessageBuffer,MessageType);break;
+    }
+    break;
+
+  /* SMS Messages */
+  case 0x14:
+    switch (MessageBuffer[3]) {
+      case 0x05:
+      case 0x06:N6110_ReplySaveSMSMessage  (MessageLength,MessageBuffer,MessageType);break;
+      case 0x08:
+      case 0x09:N6110_ReplyGetSMSMessage   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x0b:N6110_ReplyDeleteSMSMessage(MessageLength,MessageBuffer,MessageType);break;
+      case 0x37:
+      case 0x38:N6110_ReplyGetSMSStatus    (MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* WAP */
+  case 0x3f:
+    switch (MessageBuffer[3]) {
+      case 0x01:
+      case 0x02:N7110_ReplyEnableWAPCommands(MessageLength,MessageBuffer,MessageType);break;
+      case 0x07:
+      case 0x08:N7110_ReplyGetWAPBookmark   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x0a:
+      case 0x0b:N7110_ReplySetWAPBookmark   (MessageLength,MessageBuffer,MessageType);break;
+      case 0x16:
+      case 0x17:
+      case 0x1c:N7110_ReplyGetWAPSettings   (MessageLength,MessageBuffer,MessageType);break;
+      default  :unknown=true;break;
+    }
+    break;
+
+  /* Internal phone functions? */
+  case 0x40:
+    switch (MessageBuffer[2]) {
+      case 0x64:N6110_ReplyEnableExtendedCommands  (MessageLength,MessageBuffer,MessageType);break;
+      case 0x65:N6110_ReplyResetPhoneSettings      (MessageLength,MessageBuffer,MessageType);break;
+      case 0x66:N6110_ReplyIMEI                    (MessageLength,MessageBuffer,MessageType);break;
+      case 0x6a:N6110_ReplyGetProductProfileSetting(MessageLength,MessageBuffer,MessageType);break;
+      case 0x6b:N6110_ReplySetProductProfileSetting(MessageLength,MessageBuffer,MessageType);break;
+      case 0x6e:N6110_ReplyGetSecurityCode         (MessageLength,MessageBuffer,MessageType);break;
+      case 0x7e:N6110_ReplyNetmonitor              (MessageLength,MessageBuffer,MessageType);break;
+      case 0x8a:N6110_ReplySimlockInfo             (MessageLength,MessageBuffer,MessageType);break;
+      case 0x8b:N6110_ReplySetOperatorName         (MessageLength,MessageBuffer,MessageType);break;
+      case 0x8c:N6110_ReplyGetOperatorName         (MessageLength,MessageBuffer,MessageType);break;
+      case 0x8f:N6110_ReplyPlayTone                (MessageLength,MessageBuffer,MessageType);break;
+      case 0x9e:N6110_ReplyGetBinRingtone          (MessageLength,MessageBuffer,MessageType);break;
+      case 0xa0:N6110_ReplySetBinRingtone          (MessageLength,MessageBuffer,MessageType);break;
+      case 0xc8:N6110_ReplyHW                      (MessageLength,MessageBuffer,MessageType);break;
+      default  :N6110_Dispatch0x40Message          (MessageLength,MessageBuffer,MessageType);break;
+    }
+    break;
+
+  /* Picture Images */
+  case 0x47:
+
+    N6110_Dispatch0x47Message(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  /* Mobile phone identification */
+  case 0x64:
+
+    N6110_ReplyGetAuthentication(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  /***** Acknowlegment of our frames. *****/
+  case FBUS_FRTYPE_ACK:
+
+    N6110_DispatchACKMessage(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  /***** Power on message. *****/
+  case 0xd0:
+
+    N6110_Dispatch0xD0Message(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  case 0xd2:
+
+    N6110_ReplyID(MessageLength, MessageBuffer, MessageType);
+    break;
+  
+  /***** RLP frame received. *****/
+  case 0xf1:
+
+    N6110_RX_HandleRLPMessage(MessageBuffer);
+    break;
+
+  /***** Power on message. *****/
+  case 0xf4:
+
+    N6110_Dispatch0xF4Message(MessageLength, MessageBuffer, MessageType);
+    break;
+
+  /***** Unknown message *****/
+  /* If you think that you know the exact meaning of other messages - please
+     let us know. */
+  default:
+
+#ifdef DEBUG
+    fprintf(stdout, _("Message: Unknown message type.\n"));
+#endif /* DEBUG */
+    AppendLogText("Unknown msg type\n",false);
+
+    unknown=false;
+    break;
+
+  }
+
+  if (unknown) {
+#ifdef DEBUG
+    fprintf(stdout, _("Unknown message of type %02x.\n"),MessageType);
+#endif
+    AppendLogText("Unknown msg\n",false);
+  }
+}
diff --git a/common/protocol/fbus.c b/common/protocol/fbus.c
new file mode 100644 (file)
index 0000000..87e98fe
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+
+  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 an API for support for FBUS protocol
+
+*/
+
+/* "Turn on" prototypes in fbus.h */
+#define __fbus_c 
+
+/* System header files */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#ifdef WIN32
+  #include <windows.h>
+  #include "misc_win32.h"
+#else
+  #include <ctype.h>
+#endif
+
+/* Various header file */
+#include "devices/device.h"
+#include "gsm-api.h"
+#include "protocol/fbus.h"
+#include "protocol/at.h"
+#include "newmodules/newat.h"
+#include "newmodules/n6110.h"
+#include "misc.h"
+
+GSM_Protocol FBUS_Functions = {
+  FBUS_Initialise,
+  FBUS_SendMessage,
+  FBUS_SendFrame,
+  NULL_WritePhone,
+  FBUS_Terminate,
+  FBUS_RX_StateMachine
+};
+
+/* Local variables */
+enum FBUS_RX_States RX_State;
+
+u8 MessageDestination, MessageSource;
+
+u16 BufferCount, MultiBufferCount;
+
+u16 MessageLength, MultiMessageLength;
+
+bool RX_Multiple = false;
+
+u8 MessageType,MultiMessageType;
+
+u8 MessageBuffer[FBUS_MAX_RECEIVE_LENGTH * 6],MultiMessageBuffer[FBUS_MAX_RECEIVE_LENGTH * 6];
+
+u8        RequestSequenceNumber = 0x00;
+
+#ifdef DEBUG   
+char *N61_PrintDevice(int Device)
+{
+  switch (Device) {
+
+  case FBUS_DEVICE_PHONE:return _("Phone");
+  case FBUS_DEVICE_PC   :return _("PC");
+  default               :return _("Unknown");
+  }
+}
+#endif /* DEBUG */
+
+/* N61_RX_DisplayMessage is called when a message we don't know about is
+   received so that the user can see what is going back and forth, and perhaps
+   shed some more light/explain another message type! */
+void N61_RX_DisplayMessage()
+{
+#ifdef DEBUG
+  fprintf(stdout, _("Msg Dest: %s\n"), N61_PrintDevice(MessageDestination));
+  fprintf(stdout, _("Msg Source: %s\n"), N61_PrintDevice(MessageSource));
+  fprintf(stdout, _("Msg Type: %02x\n"), MessageType);
+
+  hexdump(MessageLength-2,MessageBuffer);
+#endif
+
+  AppendLog(MessageBuffer,MessageLength-2,true);
+}
+
+/* Prepares the message header and sends it, prepends the message start byte
+          (0x1e) and other values according the value specified when called.
+          Calculates checksum and then sends the lot down the pipe... */
+int FBUS_SendFrame(u16 message_length, u8 message_type, u8 *buffer) {
+
+  /* Originally out_buffer[FBUS_MAX_CONTENT_LENGTH + 2],
+     but it made problems with MBUS */
+  u8 out_buffer[1000];
+  
+  int count, current=0;
+  unsigned char        checksum;
+
+  /* FIXME - we should check for the message length ... */
+
+  /* Now construct the message header. */
+
+  if (CurrentConnectionType==GCT_FBUS)
+    out_buffer[current++] = FBUS_FRAME_ID;    /* Start of the frame indicator */
+  else
+    out_buffer[current++] = FBUS_IR_FRAME_ID; /* Start of the frame indicator */
+    
+  out_buffer[current++] = FBUS_DEVICE_PHONE; /* Destination */
+
+  out_buffer[current++] = FBUS_DEVICE_PC;    /* Source */
+
+  out_buffer[current++] = message_type; /* Type */
+
+  out_buffer[current++] = 0; /* Length1 */
+  out_buffer[current++] = message_length; /* Length2 */
+
+  /* Copy in data if any. */           
+  if (message_length != 0) {
+    memcpy(out_buffer + current, buffer, message_length);
+    current+=message_length;
+  }
+
+  /* If the message length is odd we should add pad byte 0x00 */
+  if (message_length % 2)
+    out_buffer[current++]=0x00;
+
+  /* Now calculate checksums over entire message and append to message. */
+
+  /* Odd bytes */
+  checksum = 0;
+  for (count = 0; count < current; count+=2)
+    checksum ^= out_buffer[count];
+
+  out_buffer[current++] = checksum;
+
+  /* Even bytes */
+  checksum = 0;
+  for (count = 1; count < current; count+=2)
+    checksum ^= out_buffer[count];
+
+  out_buffer[current++] = checksum;
+  
+#ifdef DEBUG
+  NULL_TX_DisplayMessage(current, out_buffer);
+#endif /* DEBUG */
+
+  /* Send it out... */
+  if (!NULL_WritePhone(current,out_buffer))
+    return (false);
+
+  return (true);
+}
+
+int FBUS_SendMessage(u16 message_length, u8 message_type, u8 *buffer) {
+  
+  u8 seqnum;
+
+  u8 frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
+
+  u8 nom, lml;  /* number of messages, last message len */
+  int i;
+
+  seqnum = 0x40 + RequestSequenceNumber;
+  RequestSequenceNumber = (RequestSequenceNumber + 1) & 0x07;
+  if (message_length > FBUS_MAX_CONTENT_LENGTH) {
+
+    nom = (message_length + FBUS_MAX_CONTENT_LENGTH - 1)
+                          / FBUS_MAX_CONTENT_LENGTH;
+    lml = message_length - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
+
+    for (i = 0; i < nom - 1; i++) {
+
+      memcpy(frame_buffer, buffer + (i * FBUS_MAX_CONTENT_LENGTH),
+             FBUS_MAX_CONTENT_LENGTH);
+      frame_buffer[FBUS_MAX_CONTENT_LENGTH] = nom - i;
+      frame_buffer[FBUS_MAX_CONTENT_LENGTH + 1] = seqnum;
+
+      FBUS_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2, message_type,
+                     frame_buffer);
+
+      seqnum = RequestSequenceNumber;
+      RequestSequenceNumber = (RequestSequenceNumber + 1) & 0x07;    
+    }
+
+    memcpy(frame_buffer, buffer + ((nom - 1) * FBUS_MAX_CONTENT_LENGTH), lml);
+    frame_buffer[lml] = 0x01;
+    frame_buffer[lml + 1] = seqnum;
+    FBUS_SendFrame(lml + 2, message_type, frame_buffer);
+
+  } else {
+
+    memcpy(frame_buffer, buffer, message_length);
+    frame_buffer[message_length] = 0x01;
+    frame_buffer[message_length + 1] = seqnum;
+    FBUS_SendFrame(message_length + 2, message_type, frame_buffer);
+  }
+
+  return (true);
+}
+
+int FBUS_SendAck(u8 message_type, u8 message_seq) {
+
+  unsigned char request[6];
+
+  request[0] = message_type;
+  request[1] = message_seq;
+
+#ifdef DEBUG
+  fprintf(stdout, _("[Sending Ack of type %02x, seq: %x]\n"), message_type, message_seq);
+#endif /* DEBUG */
+
+  return FBUS_SendFrame(2, FBUS_FRTYPE_ACK, request);
+}
+
+/* Applications should call FBUS_Terminate to shut down the FBUS thread and
+   close the serial port. */
+void FBUS_Terminate(void)
+{
+  /* Request termination of thread */
+  CurrentRequestTerminate = true;
+
+  /* Close serial port. */
+  device_close();
+}
+
+/* RX_State machine for receive handling.  Called once for each character
+   received from the phone/phone. */
+
+void FBUS_RX_StateMachine(unsigned char rx_byte) {
+
+  static struct timeval time_now, time_last, time_diff;
+  
+  static int checksum[2];
+  
+  int i=0;
+
+//  if (CurrentConnectionType==GCT_DLR3) {
+//    AT_RX_StateMachine(rx_byte);
+//  } else {
+
+#ifdef DEBUG
+  /* For model sniff only display received bytes */
+  if (strcmp(GSM_Info->FBUSModels, "sniff"))
+  {
+#endif
+
+  /* XOR the byte with the current checksum */
+  checksum[BufferCount&1] ^= rx_byte;
+
+  switch (RX_State) {
+       
+    /* Messages from the phone start with an 0x1e (FBUS) or 0x1c (IR) or 0x1f (MBUS).
+       We use this to "synchronise" with the incoming data stream. However,
+       if we see something else, we assume we have lost sync and we require
+       a gap of at least 5ms before we start looking again. This is because
+       the data part of the frame could contain a byte which looks like the
+       sync byte */
+
+  case FBUS_RX_Discarding:
+
+#ifndef VC6
+    gettimeofday(&time_now, NULL);
+    timersub(&time_now, &time_last, &time_diff);
+    if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
+      time_last = time_now;  /* no gap seen, continue discarding */
+      break;
+    }
+    /* else fall through to... */
+#endif
+
+  case FBUS_RX_Sync:
+
+      if ((CurrentConnectionType==GCT_FBUS && rx_byte == FBUS_FRAME_ID) ||
+          ((CurrentConnectionType==GCT_Infrared ||
+          CurrentConnectionType==GCT_Tekram) && rx_byte == FBUS_IR_FRAME_ID)) {
+
+        BufferCount = 0;
+
+       RX_State = FBUS_RX_GetDestination;
+       
+       /* Initialize checksums. */
+       checksum[0] = rx_byte;
+       checksum[1] = 0;
+      } else {
+        /* Lost frame sync */
+        RX_State = FBUS_RX_Discarding;
+#ifndef VC6
+        gettimeofday(&time_last, NULL);
+#endif
+      }    
+    break;
+
+  case FBUS_RX_GetDestination:
+
+    MessageDestination=rx_byte;
+    RX_State = FBUS_RX_GetSource;
+
+    /* When there is a checksum error and things get out of sync we have to manage to resync */
+    /* If doing a data call at the time, finding a 0x1e etc is really quite likely in the data stream */
+    /* Then all sorts of horrible things happen because the packet length etc is wrong... */
+    /* Therefore we test here for a destination of 0x0c and return to the top if it is not */
+    if (rx_byte!=FBUS_DEVICE_PC && strstr(GSM_Info->FBUSModels, "sniff")==NULL) {
+      RX_State=FBUS_RX_Sync;
+#ifdef DEBUG
+      fprintf(stdout,"The fbus stream is out of sync - expected 0x0c, got %2x\n",rx_byte);
+#endif
+      AppendLogText("SYNC\n",false);
+    }
+
+    break;
+
+  case FBUS_RX_GetSource:
+
+    MessageSource=rx_byte;
+    RX_State = FBUS_RX_GetType;
+
+    /* Source should be 0x00 */
+    if (rx_byte!=FBUS_DEVICE_PHONE && strstr(GSM_Info->FBUSModels, "sniff")==NULL)  {
+      RX_State=FBUS_RX_Sync;
+#ifdef DEBUG
+      fprintf(stdout,"The fbus stream is out of sync - expected 0x00, got %2x\n",rx_byte);
+#endif
+      AppendLogText("SYNC\n",false);
+    }
+    
+    break;
+
+  case FBUS_RX_GetType:
+
+    MessageType=rx_byte;
+
+    RX_State = FBUS_RX_GetLength1;
+
+    break;
+
+  case FBUS_RX_GetLength1:
+
+    MessageLength = 0;
+
+    RX_State = FBUS_RX_GetLength2;
+    
+    break;
+    
+  case FBUS_RX_GetLength2:
+
+    /* MW:Here are problems with conversion. For chars 0-127 it's OK, for
+       higher not (probably because rx_byte is char type) - improtant
+       for MBUS. So, I make it double and strange - generally it should be
+       more simple and make simple convert rx_byte into MessageLength */      
+#if defined(__svr4__) || defined(__FreeBSD__)
+    if (rx_byte!=0) {
+      for (i=0;i<rx_byte;i++)
+       MessageLength=MessageLength++;
+    }
+#else
+    MessageLength = rx_byte;
+#endif
+    
+    RX_State = FBUS_RX_GetMessage;
+    
+    break;
+    
+  case FBUS_RX_GetMessage:
+
+    MessageBuffer[BufferCount] = rx_byte;
+    BufferCount ++;
+    
+    if (BufferCount>FBUS_MAX_RECEIVE_LENGTH*6) {
+#ifdef DEBUG
+      fprintf(stdout, "FB61: Message buffer overun - resetting\n");
+#endif
+      AppendLogText("OVERUN\n",false);
+      RX_Multiple=false;
+      RX_State = FBUS_RX_Sync;
+      break;
+    }
+
+    /* If this is the last byte, it's the checksum. */
+    if (BufferCount == MessageLength+(MessageLength%2)+2) {
+
+      /* Is the checksum correct? */
+      if (checksum[0] == checksum[1]) {
+         
+        if (RX_Multiple) {
+
+         if (MessageType==MultiMessageType) {
+
+            if (MessageLength+MultiMessageLength>FBUS_MAX_RECEIVE_LENGTH*6) {
+#ifdef DEBUG
+              fprintf(stdout, "FB61: Message buffer overun - resetting\n");
+#endif
+              AppendLogText("OVERUN\n",false);
+              RX_Multiple=false;
+              RX_State = FBUS_RX_Sync;
+              break;
+            }
+       
+           /* We copy next part of multiframe message into special buffer */
+           for (i=0;i<MessageLength;i++) {
+             MultiMessageBuffer[i+MultiMessageLength]=MessageBuffer[i];
+           }
+           MultiMessageLength=MultiMessageLength+MessageLength-2;
+
+            FBUS_SendAck(MessageType, MessageBuffer[MessageLength-1] & 0x0f);          
+               
+            if ((MessageLength > 1) && (MessageBuffer[MessageLength-2] != 0x01))
+            {
+            } else {
+              for (i=0;i<MultiMessageLength+2;i++) {
+               MessageBuffer[i]=MultiMessageBuffer[i];
+             }
+             MessageLength=MultiMessageLength+2;
+             RX_Multiple=false;
+
+              /* Do not debug Ack and RLP frames to detail. */
+              if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1)
+                N61_RX_DisplayMessage();
+
+              GSM->DispatchMessage(MessageLength-2, MessageBuffer, MessageType);
+            }
+
+         } else {
+
+            /* We do not want to send ACK of ACKs and ACK of RLP frames. */
+            if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1) {
+              FBUS_SendAck(MessageType, MessageBuffer[MessageLength-1] & 0x0f);
+
+              if ((MessageLength > 1) && (MessageBuffer[MessageLength-2] != 0x01))
+              {
+#ifdef DEBUG
+                fprintf(stdout,_("Multiframe message in multiframe message !\n"));
+                fprintf(stdout,_("Please report it !\n"));
+#endif
+                RX_State = FBUS_RX_Sync;
+              }
+            }
+         }
+        } else {
+
+          /* We do not want to send ACK of ACKs and ACK of RLP frames. */
+          if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1) {
+            FBUS_SendAck(MessageType, MessageBuffer[MessageLength-1] & 0x0f);
+
+            if ((MessageLength > 1) && (MessageBuffer[MessageLength-2] != 0x01))
+            {
+              /* We copy previous part of multiframe message into special buffer */
+              RX_Multiple = true;
+              for (i=0;i<MessageLength-2;i++) {
+               MultiMessageBuffer[i]=MessageBuffer[i];
+             }
+             MultiMessageLength=MessageLength-2;
+             MultiMessageType=MessageType;
+           }
+         }
+
+          if (!RX_Multiple && MessageDestination!=FBUS_DEVICE_PHONE ) {
+            /* Do not debug Ack and RLP frames to detail. */
+            if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1)
+              N61_RX_DisplayMessage();
+
+            GSM->DispatchMessage(MessageLength-2, MessageBuffer, MessageType);
+          }
+
+#ifdef DEBUG
+          /* When make debug and message is to phone display it */
+         if (MessageDestination==FBUS_DEVICE_PHONE) {
+            for (i=MessageLength;i>=0;i--)
+              MessageBuffer[i+6]=MessageBuffer[i];
+            MessageBuffer[0]=FBUS_FRAME_ID;
+            MessageBuffer[1]=FBUS_DEVICE_PHONE;
+            MessageBuffer[2]=FBUS_DEVICE_PC;
+            MessageBuffer[3]=MessageType;
+            MessageBuffer[4]=0;
+            MessageBuffer[5]=MessageLength;
+            MessageLength=MessageLength+8;
+            if (MessageLength % 2) MessageLength++;
+           NULL_TX_DisplayMessage(MessageLength, MessageBuffer);
+          }
+#endif   
+       }
+      } else {
+#ifdef DEBUG
+          fprintf(stdout, _("Bad checksum %02x (should be %02x), msg len=%i !\n"),checksum[0],checksum[1],MessageLength);
+#endif /* DEBUG */
+        AppendLogText("CHECKSUM\n",false);
+
+       /* Just to be sure! */
+       RX_Multiple=false;
+      }
+      RX_State = FBUS_RX_Sync;    
+    }
+    break;
+  }
+
+#ifdef DEBUG
+  } else {
+    if (isprint(rx_byte))
+      fprintf(stdout, "[%02x%c]", rx_byte, rx_byte);
+    else
+      fprintf(stdout, "[%02x ]", rx_byte);
+
+  }
+#endif
+
+//  }
+}
+  
+/* Called by initialisation code to open comm port in asynchronous mode. */
+bool FBUS_OpenSerial(void)
+{
+  /* Uncomment, if want to test first method for DLR3 */
+//  unsigned char req[3] = {"AT\r"};  
+//  unsigned char req2[5] = {"AT&F\r"};  
+//  unsigned char req3[13] = {"AT*NOKIAFBUS\r"};  
+
+//  GSM_Error error;
+
+//  GSM_Information *GSMINFOCOPY;
+//  GSM_Functions *GSMCOPY;
+
+  switch (CurrentConnectionType) {
+     case GCT_FBUS:
+
+#ifdef DEBUG
+       fprintf(stdout, _("Setting cable for FBUS communication...\n"));
+#endif /* DEBUG */
+
+       device_changespeed(115200);
+   
+       /* Colin wrote:
+       The data suite cable has some electronics built into the connector. This of
+       course needs a power supply of some sorts to operate properly.
+
+       In this case power is drawn off the handshaking lines of the PC. DTR has to
+       be set and RTS have to be cleared, thus if you use a terminal program (that
+       does not set the handshaking lines to these conditions) you will get weird
+       results. It will not set them like this since if Request To Send (RTS) is
+       not set the other party will not send any data (in hardware handshaking)
+       and if DTS is not set (handshaking = none) the cable will not receive
+       power. */
+       /* clearing the RTS bit and setting the DTR bit*/
+       device_setdtrrts(1, 0);
+
+       break;
+     case GCT_DLR3:
+
+#ifdef DEBUG
+       fprintf(stdout, _("Setting DLR3 cable for FBUS communication...\n"));
+#endif /* DEBUG */
+
+       /* There are 2 ways to init DLR in FBUS: Here is first described by
+          Reuben Harris [reuben.harris@snowvalley.com] and used in Logo Manager,
+              1. Firstly set the connection baud to 19200, DTR off, RTS off,
+                Parity on, one stop bit, 
+              2. Send "AT\r\n". The response should be "AT\r\n\r\nOK\r\n".
+              3. Send "AT&F\r\n". The response should be "AT&F\r\n\r\nOK\r\n".
+              4. Send "AT*NOKIAFBUS\r\n". The response should be
+                 "AT*NOKIAFBUS\r\n\r\nOK\r\n".
+              5. Set speed to 115200 
+
+          This is one should be used by us, because seems to be compatible with more
+          phones. But we make something wrong and often phones don't want to start transmision */
+
+       /* Uncomment and test if want */
+//       device_changespeed(19200);
+
+       /*leave RTS low, DTR low for duration of session.*/
+//       device_setdtrrts(0, 0);
+
+       /* Making copy of pointers */
+//       GSMCOPY = GSM;
+//       GSMINFOCOPY =GSM_Info;        
+
+       /* Set pointers to relevant addresses - new "AT" module here is required */
+//       GSM = &Nat_Functions;
+//       GSM_Info = &Nat_Information;
+
+       /* Note: We change Protocol inside function pointed by it.
+       That's why in FBUS_RX_StateMachine we must check it (changing
+       Protocol is not enough), This is for correct work and be sure... */
+//       Protocol = &AT_Functions;
+
+//       error=N6110_SendMessageSequence (50, &CurrentGetHWError, 3, 0x00, req);
+//       if (error!=GE_NONE) return false;
+
+//       error=N6110_SendMessageSequence (50, &CurrentGetHWError, 5, 0x00, req2);
+//       if (error!=GE_NONE) return false;
+
+//       error=N6110_SendMessageSequence (50, &CurrentGetHWError, 13, 0x00, req3);
+//       if (error!=GE_NONE) return false;
+
+       /* Returning to old protocol */
+//       Protocol = &FBUS_Functions;
+
+       /* Returning to old module */
+//       GSM = GSMCOPY;
+//       GSM_Info = GSMINFOCOPY;        
+
+//       device_changespeed(115200);
+
+       /*  Second method for DLR3:
+               Used by some 7110 soft, but not compatible with some other
+               phones supporting DLR3 - 7160, NCP2.0
+              Used in this moment in mygnokii
+       */
+
+       device_changespeed(115200);
+
+       /*leave RTS low, DTR low for duration of session.*/
+       device_setdtrrts(0, 0);
+      
+       usleep(100000);
+
+       CurrentConnectionType=GCT_FBUS;
+
+       break;
+     case GCT_Infrared:
+       /* It's more complicated and not done here */
+       break;
+
+     case GCT_Tekram:
+       /* It's more complicated and not done here */
+       break;
+
+     default:
+#ifdef DEBUG
+       fprintf(stdout,_("Wrong connection type for fbus module. Inform marcin-wiacek@topnet.pl about it\n"));
+#endif
+       break;
+  }
+
+  return (true);
+}
+
+/* Initialise variables and state machine. */
+GSM_Error FBUS_Initialise(char *port_device, char *initlength,
+                          GSM_ConnectionType connection,
+                          void (*rlp_callback)(RLP_F96Frame *frame))
+{
+
+  if (!StartConnection (port_device,false,connection))
+    return GE_INTERNALERROR;
+      
+  CurrentConnectionType = connection;
+
+  if (FBUS_OpenSerial() != true) return GE_INTERNALERROR;
+
+  return (GE_NONE);
+}
diff --git a/gnokii/gnokii.c b/gnokii/gnokii.c
new file mode 100644 (file)
index 0000000..c8db700
--- /dev/null
@@ -0,0 +1,8913 @@
+/*
+
+  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.
+       
+  Mainline code for gnokii utility.  Handles command line parsing and
+  reading/writing phonebook entries and other stuff.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#ifndef VC6
+  #if defined(__svr4__) || defined(__FreeBSD__)
+  #  include <strings.h>       /* for bzero */
+  #endif
+#else
+  /* for VC6 make scripts save VERSION constant in mversion.h file */
+  #include "mversion.h"
+#endif
+
+#ifdef WIN32
+
+  #include <windows.h>
+
+  #include "misc_win32.h"
+  #include "getopt.h"
+
+  #define DEV_CONSOLE "CON:"
+
+#else
+
+  #include <unistd.h>
+  #include <termios.h>
+  #include <fcntl.h>
+  #include <sys/types.h>
+  #include <sys/time.h>
+  #include <getopt.h>
+
+  #define DEV_CONSOLE "/dev/tty"
+#endif
+
+#include "misc.h"
+#include "gsm-common.h"
+#include "gsm-api.h"
+#include "gsm-networks.h"
+#include "gsm-ringtones.h"
+#include "gsm-bitmaps.h"
+#include "gsm-wap.h"
+#include "gsm-sms.h"
+#include "gsm-datetime.h"
+#include "gsm-phonebook.h"
+#include "gsm-calendar.h"
+#include "gsm-coding.h"
+#include "newmodules/n6110.h"
+#include "files/cfgreader.h"
+#include "files/gsm-filetypes.h"
+#include "gnokii.h"
+
+#ifdef USE_NLS
+  #include <locale.h>
+#endif
+
+char *model;           /* Model from .gnokiirc file. */
+char *Port;            /* Port from .gnokiirc file */
+char *Initlength;      /* Init length from .gnokiirc file */
+char *Connection;      /* Connection type from .gnokiirc file */
+char *SynchronizeTime; /* If we set date and time from computer to phone (from .gnokiirc file) */
+char *BinDir;          /* Binaries directory from .gnokiirc file - not used here yet */
+
+GSM_SMSMessage SMS[4];
+  
+char *GetProfileCallAlertString(int code) {
+
+  switch (code) {
+    case PROFILE_CALLALERT_RINGING     : return "Ringing";
+    case PROFILE_CALLALERT_ASCENDING   : return "Ascending";
+    case PROFILE_CALLALERT_RINGONCE    : return "Ring once";
+    case PROFILE_CALLALERT_BEEPONCE    : return "Beep once";
+    case PROFILE_CALLALERT_CALLERGROUPS: return "Caller groups";
+    case PROFILE_CALLALERT_OFF         : return "Off";
+    default                            : return "Unknown";
+  }
+}
+
+char *GetProfileVolumeString(int code) {
+
+  switch (code) {
+    case PROFILE_VOLUME_LEVEL1 : return "Level 1";
+    case PROFILE_VOLUME_LEVEL2 : return "Level 2";
+    case PROFILE_VOLUME_LEVEL3 : return "Level 3";
+    case PROFILE_VOLUME_LEVEL4 : return "Level 4";
+    case PROFILE_VOLUME_LEVEL5 : return "Level 5";
+    default                    : return "Unknown";
+  }
+}
+
+char *GetProfileKeypadToneString(int code) {
+
+  switch (code) {
+    case PROFILE_KEYPAD_OFF    : return "Off";
+    case PROFILE_KEYPAD_LEVEL1 : return "Level 1";
+    case PROFILE_KEYPAD_LEVEL2 : return "Level 2";
+    case PROFILE_KEYPAD_LEVEL3 : return "Level 3";
+    default                    : return "Unknown";
+  }
+}
+
+char *GetProfileMessageToneString(int code) {
+
+  switch (code) {
+    case PROFILE_MESSAGE_NOTONE    : return "No tone";
+    case PROFILE_MESSAGE_STANDARD  : return "Standard";
+    case PROFILE_MESSAGE_SPECIAL   : return "Special";
+    case PROFILE_MESSAGE_BEEPONCE  : return "Beep once";
+    case PROFILE_MESSAGE_ASCENDING : return "Ascending";
+    default                        : return "Unknown";
+  }
+}
+
+char *GetProfileWarningToneString(int code) {
+
+  switch (code) {
+    case PROFILE_WARNING_OFF : return "Off";
+    case PROFILE_WARNING_ON  : return "On";
+    default                  : return "Unknown";
+  }
+}
+
+char *GetProfileOnOffString(int code) {
+
+  switch (code) {
+    case 0x00 : return "Off";
+    case 0x01 : return "On";
+    default   : return "Unknown";
+  }
+}
+
+static char *GetProfileVibrationString(int code)
+{
+       switch (code) {
+       case PROFILE_VIBRATION_OFF:     return "Off";
+       case PROFILE_VIBRATION_ON:      return "On";
+       case PROFILE_VIBRATION_FIRST:   return "Vibrate first";
+       default:                        return "Unknown";
+       }
+}
+
+char BufferProfileGroups[90];
+
+char *GetProfileCallerGroups(int code) 
+{
+  static char az_group_name[5][MAX_BITMAP_TEXT_LENGTH];
+  static bool enteronce=false;
+  register int i;
+  GSM_Bitmap bitmap;
+
+  if( code == PROFILE_CALLERGROUPS_ALL )
+       return _("All calls alert");
+
+  if( !enteronce ) {
+    for(i=0;i<5;i++) az_group_name[i][0]='\0';
+    enteronce=true;
+  }
+
+  BufferProfileGroups[0]=0;
+    
+  for(i=0;i<5;i++)
+  {
+    int code2test;
+    char z_gtype[12];
+
+    code2test=(i==0) ? 1 : 2<<(i-1);
+
+    if( code & code2test )
+    {
+      if (!strcmp(az_group_name[i],"")) 
+      {
+        if (GetModelFeature (FN_CALLERGROUPS)!=0) {
+          bitmap.type=GSM_CallerLogo;
+          bitmap.number=i;
+          strcpy(z_gtype,_("unknown"));
+          if (GSM->GetBitmap(&bitmap)==GE_NONE)
+            strcpy( az_group_name[i], bitmap.text );
+        }
+        if ((!strcmp(az_group_name[i],""))) {
+            switch(i) {
+            case 0:strcpy(az_group_name[i],_("Family"));break;
+            case 1:strcpy(az_group_name[i],_("VIP"));break;
+            case 2:strcpy(az_group_name[i],_("Friends"));break;
+            case 3:strcpy(az_group_name[i],_("Colleagues"));break;
+            case 4:strcpy(az_group_name[i],_("Other"));break;
+            default:break;
+          }
+        }
+      }
+      strcpy(z_gtype,az_group_name[i]);
+
+      if( strlen(BufferProfileGroups) ) strcat(BufferProfileGroups,"+");
+      strcat(BufferProfileGroups, z_gtype);
+    }
+    
+  }
+
+  return BufferProfileGroups;
+}
+
+char *print_error(GSM_Error e)
+{
+
+//     case GE_DEVICEOPENFAILED:         return "Couldn't open specified serial device.";
+//     case GE_UNKNOWNMODEL:             return "Model specified isn't known/supported.";
+//     case GE_NOLINK:                   return "Couldn't establish link with phone.";
+//     case GE_TRYAGAIN:                 return "Try again.";
+//     case GE_INVALIDSMSLOCATION:       return "Invalid SMS location.";
+//     case GE_INVALIDPHBOOKLOCATION:    return "Invalid phonebook location.";
+//     case GE_INVALIDMEMORYTYPE:        return "Invalid type of memory.";
+//     case GE_INVALIDSPEEDDIALLOCATION: return "Invalid speed dial location.";
+//     case GE_INVALIDCALNOTELOCATION:   return "Invalid calendar note location.";
+//     case GE_INVALIDDATETIME:          return "Invalid date, time or alarm specification.";
+//     case GE_EMPTYSMSLOCATION:         return "SMS location is empty.";
+//     case GE_PHBOOKNAMETOOLONG:        return "Phonebook name is too long.";
+//     case GE_PHBOOKNUMBERTOOLONG:      return "Phonebook number is too long.";
+//     case GE_PHBOOKWRITEFAILED:        return "Phonebook write failed.";
+//     case GE_SMSSENDOK:                return "SMS was send correctly.";
+//     case GE_SMSSENDFAILED:            return "SMS send fail.";
+//     case GE_SMSTOOLONG:               return "SMS message too long.";
+//     case GE_NONEWCBRECEIVED:          return "Attempt to read CB when no new CB received";
+//     case GE_INTERNALERROR:            return "Problem occured internal to model specific code.";
+//     case GE_NOTSUPPORTED:             return "Function not supported by the phone";
+//     case GE_BUSY:                     return "Command is still being executed.";
+//     case GE_USERCANCELED:             return "User has cancelled the action.";   
+//     case GE_UNKNOWN:                  return "Unknown error - well better than nothing!!";
+//     case GE_MEMORYFULL:               return "Memory is full";
+//     case GE_LINEBUSY:                 return "Outgoing call requested reported line busy";
+//     case GE_NOCARRIER:                return "No Carrier error during data call setup ?";
+
+       switch (e) {
+       case GE_NONE:                     return "No error, done OK";
+       case GE_INVALIDSECURITYCODE:      return "Invalid Security code.";
+       case GE_NOTIMPLEMENTED:           return "Called command is not implemented for the used model. Please contact marcin-wiacek@topnet.pl, if you want to help in implementing it";
+       case GE_TIMEOUT:                  return "Command timed out.";
+       case GE_CANTOPENFILE:             return "Can't open file with bitmap/ringtone";
+       case GE_SUBFORMATNOTSUPPORTED:    return "Subformat of file not supported";
+       case GE_WRONGNUMBEROFCOLORS:      return "Wrong number of colors in specified bitmap file (only 2 colors files supported)";
+       case GE_WRONGCOLORS:              return "Wrong colors in bitmap file";
+       case GE_INVALIDIMAGESIZE:         return "Invalid size of bitmap (in file, sms etc.)";
+       case GE_INVALIDFILEFORMAT:        return "Invalid format of file";
+        case GE_TOOSHORT:                 return "File too short";
+        case GE_INSIDEBOOKMARKSMENU:      return "Inside WAP Bookmarks menu. Please leave it and try again";
+        case GE_INVALIDBOOKMARKLOCATION:  return "Invalid or empty WAP bookmark location";
+        case GE_INSIDESETTINGSMENU:       return "Inside WAP Settings menu. Please leave it and try again";
+        case GE_INVALIDSETTINGSLOCATION:  return "Invalid or empty WAP settings location";
+       default:                          return "Unknown error.";
+       }
+}
+
+
+GSM_Error GSM_ReadRingtoneFileOnConsole(char *FileName, GSM_Ringtone *ringtone)
+{
+  GSM_Error error;
+  
+  error=GSM_ReadRingtoneFile(FileName, ringtone);
+  
+  switch (error) {
+    case GE_CANTOPENFILE:
+    case GE_SUBFORMATNOTSUPPORTED:
+      fprintf(stderr, _("File \"%s\"\nError: %s\n"),FileName,print_error(error));
+      break;
+    default:
+      break;
+  }
+  
+  return error;
+}
+
+GSM_Error GSM_SaveRingtoneFileOnConsole(char *FileName, GSM_Ringtone *ringtone)
+{
+  int confirm,confirm2;
+  char ans[4];
+  struct stat buf;
+  GSM_Error error;
+
+  /* Ask before overwriting */
+  while (stat(FileName, &buf) == 0) {
+  
+    confirm=-1;
+    confirm2=-1;
+    
+    while (confirm < 0) {
+      fprintf(stderr, _("Saving ringtone. File \"%s\" exists. (O)verwrite, create (n)ew or (s)kip ? "),FileName);
+      GetLine(stdin, ans, 4);
+      if (!strcmp(ans, "O") || !strcmp(ans, "o")) confirm = 1;
+      if (!strcmp(ans, "N") || !strcmp(ans, "n")) confirm = 2;
+      if (!strcmp(ans, "S") || !strcmp(ans, "s")) return GE_USERCANCELED;
+    }  
+    if (confirm==1) break;
+    if (confirm==2) {
+      while (confirm2 < 0) {
+        fprintf(stderr, _("Enter name of new file: "));
+        GetLine(stdin, FileName, 50);
+        if (&FileName[0]==0) return GE_USERCANCELED;
+       confirm2=1;
+      }  
+    }
+  }
+  
+  error=GSM_SaveRingtoneFile(FileName,ringtone);
+  
+  switch (error) {
+    case GE_CANTOPENFILE:        fprintf(stderr, _("Failed to write file \"%s\"\n"),FileName);
+                                 break;
+    default:                     break;
+  }
+  
+  return error;
+}
+
+GSM_Error GSM_ReadBitmapFileOnConsole(char *FileName, GSM_Bitmap *bitmap)
+{
+  GSM_Error error;
+  
+  error=GSM_ReadBitmapFile(FileName, bitmap);
+
+  switch (error) {
+    case GE_CANTOPENFILE:
+    case GE_WRONGNUMBEROFCOLORS:
+    case GE_WRONGCOLORS:        
+    case GE_INVALIDFILEFORMAT:  
+    case GE_SUBFORMATNOTSUPPORTED:
+    case GE_TOOSHORT:
+    case GE_INVALIDIMAGESIZE:
+      fprintf(stderr, _("File \"%s\"\nError: %s\n"),FileName,print_error(error));
+      break;
+    default: 
+      break;
+  }
+  
+  return error;
+}
+
+GSM_Error GSM_SaveBitmapFileOnConsole(char *FileName, GSM_Bitmap *bitmap)
+{
+  int confirm,confirm2;
+  char ans[4];
+  struct stat buf;
+  GSM_Error error;
+
+  /* Ask before overwriting */
+  while (stat(FileName, &buf) == 0) {
+  
+    confirm=-1;
+    confirm2=-1;
+    
+    while (confirm < 0) {
+      fprintf(stderr, _("Saving logo. File \"%s\" exists. (O)verwrite, create (n)ew or (s)kip ? "),FileName);
+      GetLine(stdin, ans, 4);
+      if (!strcmp(ans, "O") || !strcmp(ans, "o")) confirm = 1;
+      if (!strcmp(ans, "N") || !strcmp(ans, "n")) confirm = 2;
+      if (!strcmp(ans, "S") || !strcmp(ans, "s")) return GE_USERCANCELED;
+    }  
+    if (confirm==1) break;
+    if (confirm==2) {
+      while (confirm2 < 0) {
+        fprintf(stderr, _("Enter name of new file: "));
+        GetLine(stdin, FileName, 50);
+        if (&FileName[0]==0) return GE_USERCANCELED;
+       confirm2=1;
+      }  
+    }
+  }
+  
+  error=GSM_SaveBitmapFile(FileName,bitmap);
+  
+  switch (error) {
+    case GE_CANTOPENFILE:        fprintf(stderr, _("Failed to write file \"%s\"\n"),FileName);
+                                 break;
+    default:                     break;
+  }
+  
+  return error;
+}
+
+/* mode == 0 -> overwrite
+ * mode == 1 -> ask
+ * mode == 2 -> append
+ */
+int GSM_SaveTextFileOnConsole(char *FileName, char *text, int mode)
+{
+  int confirm, confirm2;
+  char ans[4];
+  struct stat buf;
+  int error;
+
+  /* Ask before overwriting */
+  if (mode==1) {
+    while (stat(FileName, &buf) == 0 && mode==1) {
+      
+      confirm=-1;
+      confirm2=-1;
+      
+      while (confirm < 0) {
+        fprintf(stderr, _("File \"%s\" exists. (O)verwrite, (a)ppend, create (n)ew or (s)kip ? "),FileName);
+        GetLine(stdin, ans, 4);
+        if (!strcmp(ans, "O") || !strcmp(ans, "o")) {
+          mode = 0;
+          confirm = 1;
+        }
+        if (!strcmp(ans, "A") || !strcmp(ans, "a")) {
+         mode = 2;
+          confirm = 1;
+        }
+        if (!strcmp(ans, "N") || !strcmp(ans, "n")) confirm=2;
+        if (!strcmp(ans, "S") || !strcmp(ans, "s")) return -1;
+      }
+      
+      if (confirm==2) {
+        while (confirm2 < 0) {
+          fprintf(stderr, _("Enter name of new file: "));
+          GetLine(stdin, FileName, 50);
+          if (&FileName[0]==0) return -1;
+         mode=1;
+         confirm2=1;
+        }  
+      }
+      
+    }  
+  }
+  
+  error=GSM_SaveTextFile(FileName, text, mode);
+  
+  switch (error) {
+    case -1: fprintf(stderr, _("Failed to write file \"%s\"\n"),  FileName);
+             break;
+    default: break;
+  }
+
+  return error;
+}
+
+int GSM_SendMultiPartSMSOnConsole(GSM_MultiSMSMessage *MultiSMS, int argnum, int argc, char *argv[],
+                                  bool unicode, bool profile, bool scale) {
+
+  int w,i;
+  
+  struct option options[] = {
+             { "smscno",       required_argument, NULL, '1'},
+             { "smsc",         required_argument, NULL, '2'},
+            { "name",         required_argument, NULL, '3'},
+             { "unicode",      no_argument,       NULL, '4'},
+             { "profilestyle", no_argument,       NULL, '5'},
+            { "scale",        no_argument,       NULL, '6'},
+             { NULL,           0,                 NULL,  0 }
+  };
+
+  GSM_Error error;  
+
+  for (w=0;w<MultiSMS->number;w++) {
+
+    if (argnum!=0) {
+      optarg = NULL;
+  
+      /* We check optional parameters from ... */
+      optind = argnum;
+
+      while ((i = getopt_long(argc, argv, "v:ds", options, NULL)) != -1) {
+        switch (i) {
+
+          case '1': /* SMSC number */
+            MultiSMS->SMS[w].MessageCenter.No = 0;
+            strcpy(MultiSMS->SMS[w].MessageCenter.Number,optarg);
+            break;
+
+          case '2': /* SMSC number index in phone memory */
+            MultiSMS->SMS[w].MessageCenter.No = atoi(optarg);
+
+            if (MultiSMS->SMS[w].MessageCenter.No < 1 || MultiSMS->SMS[w].MessageCenter.No > 5) {
+             fprintf(stderr, _("Incorrect SMSC number with \"smscno\" option (can't be <1 and >5) !\n"));
+              GSM->Terminate();
+              return -1;
+           }
+            break;
+
+         case '3': /* Receiver/recipient */
+           strncpy(MultiSMS->SMS[w].Destination,optarg,11); break;
+
+         case '4': /* Unicode */
+           if (unicode) break;
+
+         case '5': /* Profile */
+           if (profile) break;
+
+         case '6': /* Scale */
+           if (scale) break;
+
+          case 'v': /* Set validaty of SMS */
+            MultiSMS->SMS[w].Validity = atoi(optarg);
+            break;
+
+          case 'd': /* delivery report */
+            MultiSMS->SMS[w].Type=GST_DR;
+            break;     
+
+          case 's': /* Set replying via the same SMSC */
+            MultiSMS->SMS[w].ReplyViaSameSMSC = true; break;
+
+        default:
+          fprintf(stderr,_("Unknown option number %i\n"),argc);
+          GSM->Terminate();    
+          return -1;
+
+        }
+      }
+    }
+
+    error=GSM->SendSMSMessage(&MultiSMS->SMS[w]);
+
+    if (error == GE_SMSSENDOK) {
+      fprintf(stdout, _("SMS %i/%i sent OK !\n"),w+1,MultiSMS->number);
+    } else {
+      fprintf(stdout, _("SMS %i/%i, sending failed (error=%d)\n"),w+1,MultiSMS->number, error);
+    }
+
+  }
+
+  GSM->Terminate();  
+
+  return 0;
+}
+
+int GSM_SaveMultiPartSMSOnConsole(GSM_MultiSMSMessage *MultiSMS, int argnum, int argc, char *argv[],
+                                  bool inter, bool unicode, bool profile, bool scale) {
+
+  int w,i;
+  
+  GSM_SMSMessage SMSold;
+
+  struct option options[] = {
+             { "smscno",       required_argument, NULL, '1'},
+             { "smsc",         required_argument, NULL, '2'},
+            { "name",         required_argument, NULL, '3'},
+             { "unicode",      no_argument,       NULL, '4'},
+             { "profilestyle", no_argument,       NULL, '5'},
+            { "scale",        no_argument,       NULL, '6'},
+             { NULL,           0,                 NULL,  0 }
+  };
+
+  int interactive;
+  int confirm = -1;
+  char ans[8];
+
+  GSM_Error error;  
+
+  interactive = inter;
+
+  for (w=0;w<MultiSMS->number;w++) {
+
+    if (argnum!=0) {
+      optarg = NULL;
+  
+      /* We check optional parameters from ... */
+      optind = argnum;
+
+      while ((i = getopt_long(argc, argv, "risal:", options, NULL)) != -1) {
+        switch (i) {
+
+          case '1': /* SMSC number */
+            MultiSMS->SMS[w].MessageCenter.No = 0;
+            strcpy(MultiSMS->SMS[w].MessageCenter.Number,optarg);
+            break;
+
+          case '2': /* SMSC number index in phone memory */
+            MultiSMS->SMS[w].MessageCenter.No = atoi(optarg);
+
+            if (MultiSMS->SMS[w].MessageCenter.No < 1 || MultiSMS->SMS[w].MessageCenter.No > 5) {
+             fprintf(stderr, _("Incorrect SMSC number with \"smscno\" option (can't be <1 and >5) !\n"));
+              GSM->Terminate();
+              return -1;
+           }
+            break;
+
+         case '3': /* Receiver/recipient */
+           strncpy(MultiSMS->SMS[w].Destination,optarg,11); break;
+
+         case '4': /* Unicode */
+           if (unicode) break;
+
+         case '5': /* Profile */
+           if (profile) break;
+
+         case '6': /* Scale */
+           if (scale) break;
+
+          case 'r': /* mark as read */
+            MultiSMS->SMS[w].Status = GSS_SENTREAD; break;
+
+         case 'i': /* Save into Inbox */
+            MultiSMS->SMS[w].folder = GST_INBOX; break;
+         
+          case 's': /* Set replying via the same SMSC */
+            MultiSMS->SMS[w].ReplyViaSameSMSC = true; break;
+
+          case 'a': /* Ask before overwriting */
+            interactive=true;break;     
+       
+          case 'l': /* Specify location */
+            MultiSMS->SMS[0].Location = atoi(optarg); break;     
+
+        default:
+          fprintf(stderr,_("Unknown option number %i\n"),argc);
+          GSM->Terminate();    
+          return -1;
+        }
+      }
+    }
+
+    if (interactive && MultiSMS->SMS[0].Location!=0 && w==0) {
+      SMSold.Location=MultiSMS->SMS[0].Location;
+      error = GSM->GetSMSMessage(&SMSold);
+      switch (error) {
+        case GE_NONE:
+          fprintf(stderr, _("Message at specified location exists. "));
+          while (confirm < 0) {
+            fprintf(stderr, _("Overwrite? (yes/no) "));
+            GetLine(stdin, ans, 7);
+            if (!strcmp(ans, "yes")) confirm = 1;
+            if (!strcmp(ans, "no")) confirm = 0;
+          }  
+          if (!confirm) { GSM->Terminate(); return 0; }
+          else break;
+        case GE_INVALIDSMSLOCATION:
+          fprintf(stderr, _("Invalid location\n"));
+          GSM->Terminate();
+          return -1;
+        default:
+/* FIXME: Remove this fprintf when the function is thoroughly tested */
+#ifdef DEBUG
+            fprintf(stderr, _("Location %d empty. Saving\n"), SMS[w].Location);
+#endif
+          break;
+      }
+    }
+
+    error=GSM->SaveSMSMessage(&MultiSMS->SMS[w]);
+
+    if (error == GE_NONE)
+      fprintf(stdout, _("SMS %i/%i saved at location %i !\n"),w+1,MultiSMS->number,MultiSMS->SMS[w].MessageNumber);
+    else
+      fprintf(stdout, _("SMS %i/%i saving failed (error=%d, location=%i)\n"), w+1, MultiSMS->number, error,MultiSMS->SMS[w].Location);
+  }
+
+  GSM->Terminate();  
+
+  return 0;
+}
+
+void GSM_PlayRingtoneOnConsole(GSM_Ringtone *ringtone)
+{
+  int i;
+#ifdef VC6
+  char mychar;
+#endif
+
+  for (i=0;i<ringtone->NrNotes;i++) {
+#ifdef VC6
+    if (_kbhit()) {
+      mychar=_getch();
+      break;
+    }
+#endif
+    GSM_PlayOneNote (ringtone->notes[i]);
+  }
+  GSM->PlayTone(255*255,0);
+}
+
+/* This function shows the copyright and some informations usefull for
+   debugging. */
+int version(void)
+{
+
+  fprintf(stdout, _("GNOKII Version %s\n"
+"Copyright (C) Hugh Blemings <hugh@linuxcare.com>, 1999, 2000\n"
+"Copyright (C) Pavel Janík ml. <Pavel.Janik@linux.cz>, 1999, 2000\n"
+"Built %s %s for %s on %s \n"), VERSION, __TIME__, __DATE__, model, Port);
+
+  return 0;
+}
+
+/* The function usage is only informative - it prints this program's usage and
+   command-line options. */
+
+int usage(void)
+{
+
+  fprintf(stdout, _("   usage: gnokii [--help|--monitor [-noloop|-nl]|--version]\n"
+"          gnokii --getmemory memory_type [start [end]] [-short|-v30|-v21|-v]\n"
+"          gnokii --writephonebook [-i]\n"
+"          gnokii --sendphonebookentry destination memory_type location\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+"          gnokii --savephonebookentry memory_type location\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [--name name]\n"
+"          gnokii --getvoicemailbox\n"
+"          gnokii --getspeeddial number\n"
+"          gnokii --setspeeddial number memory_type location\n\n"
+
+"          gnokii --getsms memory_type start [end] [-f file]\n"
+"          gnokii --getsmsstatus\n"
+"          gnokii --getsmsfolders\n"
+"          gnokii --deletesms memory_type start [end]\n"
+"          gnokii --sendsms destination [--smsc message_center_number |\n"
+"                 --smscno message_center_index] [--long n] [-s] [-C n]\n"
+"                 [--enablevoice|--disablevoice|--enablefax|--disablefax|\n"
+"                  --enableemail|--disableemail|--void][--unicode][-v n][-d]\n"
+"          gnokii --savesms destination|\"\" [--smsc \n"
+"                 message_center_number] [--smscno message_center_index]\n"
+"                 [--long n] [-r] [-i] [-s][-C n][-a][-l][F n][--smsname name]\n"
+"                 [--enablevoice|--disablevoice|--enablefax|--disablefax|\n"
+"                  --enableemail|--disableemail|--void|--hang|--bug][--unicode]\n"
+"          gnokii --receivesms\n"
+"          gnokii --getsmsc message_center_number\n"
+"          gnokii --renamesmsc number new_name\n\n"
+
+"          gnokii --setdatetime [YYYY [MM [DD [HH [MM]]]]]\n"
+"          gnokii --getdatetime\n"
+"          gnokii --setalarm HH MM\n"
+"          gnokii --getalarm\n\n"
+
+"          gnokii --getcalendarnote { start end [-v30|-v10] | --short|-s }\n"
+"          gnokii --writecalendarnote vcardfile number\n"
+"          gnokii --deletecalendarnote index\n"
+"          gnokii --sendcalendarnote destination vcardfile number\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+"          gnokii --savecalendarnote vcardfile number\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [--name name]\n\n"
+
+"          gnokii --netmonitor {reset|off|field|devel|next|nr}\n"
+"          gnokii --nm_collect screen1|-d [screen2|-d]...\n"
+"          gnokii --netmonitordata [-S file] [-I file] [-h] [-n n] [-ts n][-tm n]\n"
+"                 [-fs str] [-ls str] FLD1:FLD2:FLDn:... \n"
+"                 (see files netmonitordata_????_??? for details)\n\n"
+
+"          gnokii --bitmapconvert source destination\n"
+"          gnokii --bitmapconvert source destination op|7110op [network code]\n"
+"          gnokii --bitmapconvert source destination caller [caller group number]\n"
+"          gnokii --bitmapconvert source destination\n"
+"                   startup|7110startup|6210startup\n"
+"          gnokii --bitmapconvert source destination picture\n"
+"          gnokii --showbitmap logofile\n"
+"          gnokii --sendlogo op destination logofile network_code\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+"          gnokii --sendlogo picture destination logofile text\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d] [--unicode]\n"
+"          gnokii --sendlogo screensaver destination logofile\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+"          gnokii --sendlogo caller destination logofile\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+"          gnokii --savelogo op logofile network_code\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [-l] [--name name]\n"
+"          gnokii --savelogo picture logofile text\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [-l] [--name name] [--unicode]\n"
+"          gnokii --savelogo screensaver logofile\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [-l] [--name name]\n"
+"          gnokii --savelogo caller logofile\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [-l] [--name name]\n"
+"          gnokii --setlogo op|7110op [logofile] [network code]\n"
+"          gnokii --setlogo startup|7110startup|6210startup [logofile]\n"
+"          gnokii --setlogo startup 1|2|3\n"
+"          gnokii --setlogo caller [logofile] [caller group number] [group name]\n"
+"          gnokii --setlogo picture [logofile] [number] [text] [sender]\n"
+"          gnokii --setlogo {dealer|text} [text]\n"
+"          gnokii --getlogo op|7110op [logofile] [network code]\n"
+"          gnokii --getlogo startup|7110startup|6210startup [logofile]\n"
+"          gnokii --getlogo caller [logofile][caller group number]\n"
+"          gnokii --getlogo picture [logofile][number]\n"
+"          gnokii --getlogo {dealer|text}\n\n"
+
+"          gnokii --sendringtone destination ringtonefile\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d] [--scale] [--profilestyle]\n"
+"          gnokii --saveringtone ringtonefile\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [--name name] [--scale] [--profilestyle]\n"
+"          gnokii --setringtone ringtonefile [location]\n"
+"          gnokii --getringtone ringtonefile [location]\n"
+"          gnokii --ringtoneconvert source destination\n"
+"          gnokii --binringtoneconvert source destination\n"
+"          gnokii --playringtone ringtonefile\n"
+"          gnokii --composer ringtonefile\n"
+"          gnokii --allringtones\n\n"
+
+"          gnokii --getprofile [number]\n"
+"          gnokii --setprofile number feature value\n"
+"          gnokii --sendprofile destination profile_name ringtonefile\n"
+"                 picturefile [--smsc message_center_number]\n"
+"                 [--smscno message_center_index] [-s] [-v n] [-d] [--scale]\n\n"
+
+"          gnokii --reset [soft|hard]\n"
+"          gnokii --dialvoice number\n"
+"          gnokii --cancelcall\n"
+"          gnokii --displayoutput\n"
+"          gnokii --presskeysequence sequence\n"
+"          gnokii --backupsettings file\n"
+"          gnokii --restoresettings file\n"
+"          gnokii --getphoneprofile\n"
+"          gnokii --setphoneprofile feature value\n"
+"          gnokii --getoperatorname\n"
+"          gnokii --setoperatorname code name\n"
+"          gnokii --senddtmf string\n"
+"          gnokii --divert register|enable|query|disable|erasure\n"
+"                 all|busy|noans|outofreach all|voice|fax|data\n"
+"                 [number timeout]\n\n"
+
+"          gnokii --phonetests\n"
+"          gnokii --simlock\n"
+"          gnokii --getdisplaystatus\n"
+"          gnokii --identify\n\n"
+
+"          gnokii --getwapbookmark location\n"
+"          gnokii --setwapbookmark title url [location]\n"
+"          gnokii --sendwapbookmark location destination\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+"          gnokii --savewapbookmark location\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [-l] [--name name]\n"
+"          gnokii --getwapsettings location\n"
+"          gnokii --savewapsettings location\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-r] [-i] [-s] [-a] [-l] [--name name]\n"
+"          gnokii --sendwapsettings location destination\n"
+"                 [--smsc message_center_number] [--smscno message_center_index]\n"
+"                 [-s] [-v n] [-d]\n"
+  ));
+
+#ifdef SECURITY
+  fprintf(stdout, _(
+"\n          gnokii --entersecuritycode PIN|PIN2|PUK|PUK2\n"
+"          gnokii --getsecuritycodestatus\n"
+"          gnokii --getsecuritycode PIN|PIN2|PUK|PUK2|SecurityCode\n"
+"          gnokii --geteeprom\n"
+"          gnokii --resetphonesettings\n"
+  ));
+#endif
+
+#ifdef DEBUG
+  fprintf(stdout, _(
+"          gnokii --sniff [port]\n"
+"          gnokii --decodefile file\n"
+"          gnokii --getbinringfromfile file name offset file2\n"
+  ));
+#endif
+
+  return 0;
+}
+
+/* fbusinit is the generic function which waits for the FBUS link. The limit
+   is 10 seconds. After 10 seconds we quit. */
+
+void fbusinit(void (*rlp_handler)(RLP_F96Frame *frame))
+{
+
+  int count=0;
+  GSM_Error error;
+
+#ifndef WIN32
+  if (strcmp(GetMygnokiiVersion(),VERSION)!=0)
+    fprintf(stderr,_("WARNING: version of installed libmygnokii.so (%s) is different to version of gnokii (%s)\n"),GetMygnokiiVersion(),VERSION);
+#endif
+
+  /* Initialise the code for the GSM interface. */     
+  error = GSM_Initialise(model, Port, Initlength, GetConnectionTypeFromString(Connection), rlp_handler, SynchronizeTime);
+
+  if (error != GE_NONE) {
+    fprintf(stderr, _("GSM/FBUS init failed! (Unknown model ?). Quitting.\n"));
+    exit(-1);
+  }
+
+  /* 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"));
+    exit(-1);
+  }  
+}
+
+/* This function checks that the argument count for a given options is withing
+   an allowed range. */
+
+int checkargs(int opt, struct gnokii_arg_len gals[], int argc)
+{
+
+  int i;
+
+  /* Walk through the whole array with options requiring arguments. */
+
+  for(i = 0;!(gals[i].gal_min == 0 && gals[i].gal_max == 0); i++) {
+
+    /* Current option. */
+
+    if(gals[i].gal_opt == opt) {
+
+      /* Argument count checking. */
+
+      if(gals[i].gal_flags == GAL_XOR) {
+       if(gals[i].gal_min == argc || gals[i].gal_max == argc) return 0;
+      }
+      else {
+       if(gals[i].gal_min <= argc && gals[i].gal_max >= argc) return 0;
+      }
+
+      return 1;
+
+    }
+
+  }
+
+  /* We do not have options without arguments in the array, so check them. */
+
+  if (argc==0) return 0;
+          else return 1;
+}
+
+/* Main function - handles command line arguments, passes them to separate
+   functions accordingly. */
+
+int main(int argc, char *argv[])
+{
+
+  int c, i, rc = -1;
+  int nargc = argc-2;
+  char **nargv;
+
+  /* Every option should be in this array. */
+
+  static struct option long_options[] =
+  {
+
+    { "help",               no_argument,       NULL, OPT_HELP             },// Display usage.
+    { "version",            no_argument,       NULL, OPT_VERSION          },// Display version and build information.
+    { "getsmsfolders",      no_argument,       NULL, OPT_GETSMSFOLDERS    },// Gets SMS folders
+    { "getsmsstatus",       no_argument,       NULL, OPT_GETSMSSTATUS     },// Get SMS Folder Status
+    { "identify",           no_argument,       NULL, OPT_IDENTIFY         },// Identify
+    { "pmon",               no_argument,       NULL, OPT_PMON             },// For development purposes: run in passive monitoring mode
+    { "foogle",             no_argument,       NULL, OPT_FOOGLE           },// For development purposes: insert you function calls here
+    { "getdatetime",        no_argument,       NULL, OPT_GETDATETIME      },// Get date and time mode    
+    { "getalarm",           no_argument,       NULL, OPT_GETALARM         },// Get alarm    
+    { "cancelcall",         no_argument,       NULL, OPT_CANCELCALL       },// Cancel Call    
+    { "getdisplaystatus",   no_argument,       NULL, OPT_GETDISPLAYSTATUS },// Get display status mode    
+    { "allringtones",       no_argument,       NULL, OPT_ALLRINGTONES     },/* Displays names of available ringtones */    
+    { "displayoutput",      no_argument,       NULL, OPT_DISPLAYOUTPUT    },/* Show texts from phone's display */
+    { "getphoneprofile",    no_argument,       NULL, OPT_GETPHONEPROFILE  },/* Get/Set phone profile settings */
+    { "getoperatorname",    no_argument,       NULL, OPT_GETOPERATORNAME  },/* Get downloaded operator name */    
+    { "getvoicemailbox",    no_argument,       NULL, OPT_GETVOICEMAILBOX  },/* Gets voice mailbox number */    
+    { "phonetests",         no_argument,       NULL, OPT_PHONETESTS       },
+    { "simlockinfo",        no_argument,       NULL, OPT_SIMLOCKINFO      },    
+    { "receivesms",         no_argument,       NULL, OPT_RECEIVESMS       },    
+    { "setoperatorname",    optional_argument, NULL, OPT_SETOPERATORNAME  },/* Set downloaded operator name */    
+    { "setdatetime",        optional_argument, NULL, OPT_SETDATETIME      },// Set date and time    
+    { "writephonebook",     optional_argument, NULL, OPT_WRITEPHONEBOOK   },// Write phonebook (memory) mode
+    { "reset",              optional_argument, NULL, OPT_RESET            },// Resets the phone
+    { "monitor",            optional_argument, NULL, OPT_MONITOR          },// Monitor mode
+    { "setlogo",            optional_argument, NULL, OPT_SETLOGO          },// Set logo
+    { "getprofile",         optional_argument, NULL, OPT_GETPROFILE       },// Show profile
+    { "setalarm",           required_argument, NULL, OPT_SETALARM         },// Set alarm
+    { "dialvoice",          required_argument, NULL, OPT_DIALVOICE        },// Voice call mode
+    { "getcalendarnote",    required_argument, NULL, OPT_GETCALENDARNOTE  },// Get calendar note mode    
+    { "writecalendarnote",  required_argument, NULL, OPT_WRITECALENDARNOTE},// Write calendar note mode
+    { "sendcalendarnote",   required_argument, NULL, OPT_SENDCALENDARNOTE },
+    { "savecalendarnote",   required_argument, NULL, OPT_SAVECALENDARNOTE },
+    { "sendphonebookentry", required_argument, NULL, OPT_SENDPHONEBOOKENTRY},
+    { "savephonebookentry", required_argument, NULL, OPT_SAVEPHONEBOOKENTRY},
+    { "deletecalendarnote", required_argument, NULL, OPT_DELCALENDARNOTE  },// Delete calendar note mode    
+    { "getmemory",          required_argument, NULL, OPT_GETMEMORY        },// Get memory mode
+    { "getspeeddial",       required_argument, NULL, OPT_GETSPEEDDIAL     },// Get speed dial mode
+    { "setspeeddial",       required_argument, NULL, OPT_SETSPEEDDIAL     },// Set speed dial mode
+    { "getsms",             required_argument, NULL, OPT_GETSMS           },// Get SMS message mode
+    { "deletesms",          required_argument, NULL, OPT_DELETESMS        },// Delete SMS message mode
+    { "sendsms",            required_argument, NULL, OPT_SENDSMS          },// Send SMS message mode
+    { "savesms",            required_argument, NULL, OPT_SAVESMS          },// Save SMS message mode
+    { "sendlogo",           required_argument, NULL, OPT_SENDLOGO         },// Send logo as SMS message mode
+    { "savelogo",           required_argument, NULL, OPT_SAVELOGO         },// Save logo on SIM
+    { "sendringtone",       required_argument, NULL, OPT_SENDRINGTONE     },// Send ringtone as SMS message
+    { "saveringtone",       required_argument, NULL, OPT_SAVERINGTONE     },// Saves ringtone on SIM
+    { "setringtone",        required_argument, NULL, OPT_SETRINGTONE      },// Set ringtone    
+    { "getringtone",        required_argument, NULL, OPT_GETRINGTONE      },// Get bin/normal ringtone    
+    { "presskeysequence",   required_argument, NULL, OPT_PRESSKEYSEQUENCE },/* Presses keys in phone's display */
+    { "getsmsc",            required_argument, NULL, OPT_GETSMSC          },// Get SMS center number mode
+    { "renamesmsc",         required_argument, NULL, OPT_RENAMESMSC       },// Rename SMSC
+    { "netmonitor",         required_argument, NULL, OPT_NETMONITOR       },// NetMonitor mode
+    { "senddtmf",           required_argument, NULL, OPT_SENDDTMF         },// Send DTMF sequence
+    { "getlogo",            required_argument, NULL, OPT_GETLOGO          },// Get logo
+    { "setprofile",         required_argument, NULL, OPT_SETPROFILE       },// Set profile feature
+    { "sendprofile",        required_argument, NULL, OPT_SENDPROFILE      },// Send profile via SMS
+    { "setphoneprofile",    required_argument, NULL, OPT_SETPHONEPROFILE  },/* Get/Set phone profile settings */
+    { "restoresettings",    required_argument, NULL, OPT_RESTORESETTINGS  },//Restore various settings from one file
+    { "backupsettings",     required_argument, NULL, OPT_BACKUPSETTINGS   },//Backup various settings to one file
+    { "playringtone",       required_argument, NULL, OPT_PLAYRINGTONE     },/* Plays ringtones */    
+    { "composer",           required_argument, NULL, OPT_COMPOSER         },/* Shows ringtone like in Nokia Composer */    
+    { "ringtoneconvert",    required_argument, NULL, OPT_RINGTONECONVERT  },/* Convert ringtone files */    
+    { "binringtoneconvert", required_argument, NULL, OPT_BINRINGTONECONVERT},/* Convert binary ringtone files */    
+    { "bitmapconvert",      required_argument, NULL, OPT_BITMAPCONVERT    },/* Convert bitmap files */    
+    { "showbitmap",         required_argument, NULL, OPT_SHOWBITMAP       },    
+    { "nm_collect",         required_argument, NULL, OPT_NM_COLLECT       },// NetMonitor periodical data collection mode (newbiee)
+    { "netmonitordata",     required_argument, NULL, OPT_NETMONITORDATA   },// NetMonitor periodical data collection mode (advanced)
+    { "getwapbookmark",     required_argument, NULL, OPT_GETWAPBOOKMARK   },    
+    { "setwapbookmark",     required_argument, NULL, OPT_SETWAPBOOKMARK   },    
+    { "savewapbookmark",    required_argument, NULL, OPT_SAVEWAPBOOKMARK  },    
+    { "savewapsettings",    required_argument, NULL, OPT_SAVEWAPSETTINGS  },    
+    { "sendwapsettings",    required_argument, NULL, OPT_SENDWAPSETTINGS  },    
+    { "sendwapbookmark",    required_argument, NULL, OPT_SENDWAPBOOKMARK  },    
+    { "getwapsettings",     required_argument, NULL, OPT_GETWAPSETTINGS   },    
+    { "divert",             required_argument, NULL, OPT_DIVERT           },
+
+#ifdef SECURITY
+    { "entersecuritycode",  required_argument, NULL, OPT_ENTERSECURITYCODE    },// Enter Security Code mode
+    { "getsecuritycode",    required_argument, NULL, OPT_GETSECURITYCODE      },// Get Security Code
+  { "getsecuritycodestatus",no_argument,       NULL, OPT_GETSECURITYCODESTATUS},// Get Security Code status
+    { "geteeprom",          no_argument,       NULL, OPT_GETEEPROM            },// Gets EEPROM
+    { "resetphonesettings", no_argument,       NULL, OPT_RESETPHONESETTINGS   },// Reset phone settings
+    { "setsimlock",         no_argument,       NULL, OPT_SETSIMLOCK           },// Sets simlock
+#endif
+
+#ifdef DEBUG
+    { "sniff",              optional_argument, NULL, OPT_SNIFFER    },// Will show datas from port
+    { "decodefile",         required_argument, NULL, OPT_DECODEFILE },//decode input file
+    { "getbinringfromfile", required_argument, NULL, OPT_GETBINRINGFROMFILE },
+#endif
+
+    { 0, 0, 0, 0},
+  };
+
+  /* Every command which requires arguments should have an appropriate entry
+     in this array. */
+       
+  struct gnokii_arg_len gals[] =
+  {
+
+    { OPT_MONITOR,           0, 1, 0 },
+
+#ifdef SECURITY
+    { OPT_ENTERSECURITYCODE, 1, 1, 0 },
+    { OPT_GETSECURITYCODE,   1, 1, 0 },
+#endif
+
+#ifdef DEBUG
+    { OPT_SNIFFER,           0, 1, 0 },
+    { OPT_DECODEFILE,        1, 1, 0 },
+    { OPT_GETBINRINGFROMFILE,4, 4, 0 },
+#endif
+
+    { OPT_SETDATETIME,       0, 5, 0 },
+    { OPT_BACKUPSETTINGS,    1, 1, 0 },
+    { OPT_RESTORESETTINGS,   1, 1, 0 },
+    { OPT_SETALARM,          2, 2, 0 },
+    { OPT_DIALVOICE,         1, 1, 0 },
+    { OPT_GETCALENDARNOTE,   1, 3, 0 },
+    { OPT_WRITECALENDARNOTE, 2, 2, 0 },
+    { OPT_SAVECALENDARNOTE,  2, 9, 0 },
+    { OPT_SENDCALENDARNOTE,  3, 9, 0 },
+    { OPT_SAVEPHONEBOOKENTRY,2, 9, 0 },
+    { OPT_SENDPHONEBOOKENTRY,3, 9, 0 },
+    { OPT_DELCALENDARNOTE,   1, 1, 0 },
+    { OPT_GETMEMORY,         2, 4, 0 },
+    { OPT_GETSPEEDDIAL,      1, 1, 0 },
+    { OPT_SETSPEEDDIAL,      3, 3, 0 },
+    { OPT_GETSMS,            2, 5, 0 },
+    { OPT_DELETESMS,         2, 3, 0 },
+    { OPT_SENDSMS,           1,10, 0 },
+    { OPT_SAVESMS,           1,11, 0 },
+    { OPT_SENDLOGO,          3, 9, 0 },
+    { OPT_SAVELOGO,          2,10, 0 },
+    { OPT_SENDRINGTONE,      2, 7, 0 },
+    { OPT_SAVERINGTONE,      1, 9, 0 },
+    { OPT_GETSMSC,           1, 1, 0 },
+    { OPT_RENAMESMSC,        2, 2, 0 },
+    { OPT_NETMONITOR,        1, 1, 0 },
+    { OPT_SENDDTMF,          1, 1, 0 },
+    { OPT_SETLOGO,           1, 5, 0 },
+    { OPT_GETLOGO,           1, 4, 0 },
+    { OPT_SETRINGTONE,       1, 3, 0 },
+    { OPT_GETRINGTONE,       1, 2, 0 },
+    { OPT_PRESSKEYSEQUENCE,  1, 1, 0 },
+    { OPT_RESET,             0, 1, 0 },
+    { OPT_GETPROFILE,        0, 1, 0 },
+    { OPT_SETPROFILE,        3, 3, 0 },
+    { OPT_SENDPROFILE,       4,10, 0 },
+    { OPT_WRITEPHONEBOOK,    0, 1, 0 },
+    { OPT_PLAYRINGTONE,      1, 1, 0 },
+    { OPT_COMPOSER,          1, 1, 0 },
+    { OPT_RINGTONECONVERT,   2, 2, 0 },
+    { OPT_BINRINGTONECONVERT,2, 2, 0 },
+    { OPT_BITMAPCONVERT,     2, 4, 0 },
+    { OPT_SHOWBITMAP,        1, 1, 0 },
+    { OPT_SETOPERATORNAME,   0, 2, 0 },    
+    { OPT_SETPHONEPROFILE,   2, 2, 0 },        
+    { OPT_NM_COLLECT,        1, MAX_NM_COLLECT, 0 },
+    { OPT_NETMONITORDATA,    0,99, 0 },
+    { OPT_GETWAPBOOKMARK,    1, 1, 0 },
+    { OPT_SETWAPBOOKMARK,    2, 3, 0 },
+    { OPT_SAVEWAPBOOKMARK,   1, 9, 0 },
+    { OPT_SENDWAPBOOKMARK,   2, 9, 0 },
+    { OPT_GETWAPSETTINGS,    1, 1, 0 },
+    { OPT_SAVEWAPSETTINGS,   1, 9, 0 },
+    { OPT_SENDWAPSETTINGS,   2, 9, 0 },
+    { OPT_DIVERT,            3, 5, 0 },    
+
+    { 0, 0, 0, 0 },
+  };
+
+  opterr = 0;
+
+  /* For GNU gettext */
+
+#ifdef USE_NLS
+#ifndef VC6
+  textdomain("gnokii");
+  setlocale(LC_ALL, "pl_PL"); //here is string for Polish localisation
+#else
+  setlocale(LC_ALL, ".852"); //Polish codepage for console, not "real" WIN CP
+#endif
+
+#endif
+
+    /* Read config file */
+    if (CFG_ReadConfig(&model, &Port, &Initlength, &Connection, &BinDir, &SynchronizeTime,false) < 0) {
+       exit(-1);
+    }
+
+  /* Handle command line arguments. */
+
+  c = getopt_long(argc, argv, "", long_options, NULL);
+
+  if (c == -1) {
+
+    /* No argument given - we should display usage. */
+    usage();
+    exit(-1);
+  }
+
+  /* We have to build an array of the arguments which will be passed to the
+     functions.  Please note that every text after the --command will be
+     passed as arguments.  A syntax like gnokii --cmd1 args --cmd2 args will
+     not work as expected; instead args --cmd2 args is passed as a
+     parameter. */
+
+  if((nargv = malloc(sizeof(char *) * argc)) != NULL) {
+
+    for(i = 2; i < argc; i++)
+      nargv[i-2] = argv[i];
+       
+    if(checkargs(c, gals, nargc)) {
+
+      free(nargv);
+
+      /* Wrong number of arguments - we should display usage. */
+      usage();
+      exit(-1);
+    }
+
+#ifndef VC6
+#if defined(__svr4__)
+    /* have to ignore SIGALARM */
+    sigignore(SIGALRM);
+#endif
+#endif
+
+    switch(c) {
+
+    // First, error conditions 
+    case '?':
+      fprintf(stderr, _("Use '%s --help' for usage informations.\n"), argv[0]);
+      break;
+       
+    // Then, options with no arguments
+    case OPT_HELP:                  rc = usage();                   break;
+    case OPT_VERSION:               rc = version();                 break;
+    case OPT_MONITOR:               rc = monitormode(nargc, nargv); break;
+    case OPT_GETSMSFOLDERS:         rc = getsmsfolders();           break;
+    case OPT_GETDATETIME:           rc = getdatetime();             break;
+    case OPT_GETALARM:              rc = getalarm();                break;
+    case OPT_GETDISPLAYSTATUS:      rc = getdisplaystatus();        break;
+    case OPT_PMON:                  rc = pmon();                    break;
+    case OPT_WRITEPHONEBOOK:        rc = writephonebook(nargc, nargv);break;
+
+#ifdef SECURITY
+    case OPT_ENTERSECURITYCODE:     rc = entersecuritycode(optarg); break;
+    case OPT_GETSECURITYCODESTATUS: rc = getsecuritycodestatus();   break;
+    case OPT_GETSECURITYCODE:       rc = getsecuritycode(optarg);   break;
+    case OPT_GETEEPROM:             rc = geteeprom();               break;
+    case OPT_RESETPHONESETTINGS:    rc = resetphonesettings();      break;
+    case OPT_SETSIMLOCK:            rc = setsimlock();              break;
+#endif
+
+#ifdef DEBUG
+    case OPT_SNIFFER:               rc = sniff(nargc, nargv);       break;
+    case OPT_DECODEFILE:            rc = decodefile(nargc, nargv);  break;
+    case OPT_GETBINRINGFROMFILE:    rc = getbinringfromfile(nargc, nargv);break;
+#endif                                 
+       
+    // Now, options with arguments
+    case OPT_SETDATETIME:           rc = setdatetime(nargc, nargv); break;
+    case OPT_SETALARM:              rc = setalarm(nargv);           break;
+    case OPT_DIALVOICE:             rc = dialvoice(optarg);         break;
+    case OPT_CANCELCALL:            rc = cancelcall();              break;
+    case OPT_GETCALENDARNOTE:       rc = getcalendarnote(nargc, nargv);break;
+    case OPT_DELCALENDARNOTE:       rc = deletecalendarnote(optarg);break;
+    case OPT_SAVECALENDARNOTE:      rc = savecalendarnote(nargc, nargv);break;
+    case OPT_SENDCALENDARNOTE:      rc = sendcalendarnote(nargc, nargv);break;
+    case OPT_SAVEPHONEBOOKENTRY:    rc = savephonebookentry(nargc, nargv);break;
+    case OPT_SENDPHONEBOOKENTRY:    rc = sendphonebookentry(nargc, nargv);break;
+    case OPT_WRITECALENDARNOTE:     rc = writecalendarnote(nargv);  break;
+    case OPT_GETMEMORY:             rc = getmemory(nargc, nargv);   break;
+    case OPT_GETSPEEDDIAL:          rc = getspeeddial(optarg);      break;
+    case OPT_SETSPEEDDIAL:          rc = setspeeddial(nargv);       break;
+    case OPT_GETSMS:                rc = getsms(argc, argv);        break;
+    case OPT_GETSMSSTATUS:          rc = getsmsstatus(argc, argv);  break;
+    case OPT_DELETESMS:             rc = deletesms(nargc, nargv);   break;
+    case OPT_SENDSMS:               rc = sendsms(nargc, nargv);     break;
+    case OPT_SAVESMS:               rc = savesms(nargc, nargv);     break;
+    case OPT_DIVERT:                rc = divert(nargc, nargv);      break;
+    case OPT_SENDLOGO:              rc = sendlogo(nargc, nargv);    break;
+    case OPT_SAVELOGO:              rc = savelogo(nargc, nargv);    break;
+    case OPT_GETSMSC:               rc = getsmsc(optarg);           break;
+    case OPT_RENAMESMSC:            rc = renamesmsc(nargc,nargv);   break;
+    case OPT_NETMONITOR:            rc = netmonitor(optarg);        break;
+    case OPT_IDENTIFY:              rc = identify();                break;
+    case OPT_SETLOGO:               rc = setlogo(nargc, nargv);     break;
+    case OPT_GETLOGO:               rc = getlogo(nargc, nargv);     break;
+    case OPT_RECEIVESMS:            rc = receivesms(nargc, nargv);  break;
+    case OPT_SETRINGTONE:           rc = setringtone(nargc, nargv); break;
+    case OPT_GETRINGTONE:           rc = getringtone(nargc, nargv); break;
+    case OPT_PRESSKEYSEQUENCE:      rc = presskeysequence(nargv);   break;
+    case OPT_SENDRINGTONE:          rc = sendringtone(nargc, nargv);break;
+    case OPT_SAVERINGTONE:          rc = saveringtone(nargc, nargv);break;
+    case OPT_GETPROFILE:            rc = getprofile(nargc, nargv);  break;
+    case OPT_SETPROFILE:            rc = setprofile(nargc, nargv);  break;
+    case OPT_SENDPROFILE:           rc = sendprofile(nargc, nargv); break;
+    case OPT_DISPLAYOUTPUT:         rc = displayoutput();           break;
+    case OPT_RESTORESETTINGS:       rc = restoresettings(nargv);    break;
+    case OPT_BACKUPSETTINGS:        rc = backupsettings(nargv);     break;
+    case OPT_RINGTONECONVERT:       rc = ringtoneconvert(nargc, nargv);break;
+    case OPT_BINRINGTONECONVERT:    rc = binringtoneconvert(nargc, nargv);break;
+    case OPT_BITMAPCONVERT:         rc = bitmapconvert(nargc, nargv);break;
+    case OPT_SHOWBITMAP:            rc = showbitmap(nargc, nargv);  break;
+    case OPT_PLAYRINGTONE:          rc = playringtone(nargc, nargv);break;
+    case OPT_COMPOSER:              rc = composer(nargc, nargv);    break;
+    case OPT_FOOGLE:                rc = foogle(nargv);             break;
+    case OPT_PHONETESTS:            rc = phonetests();              break;
+    case OPT_SIMLOCKINFO:           rc = simlockinfo();             break;
+    case OPT_SENDDTMF:              rc = senddtmf(optarg);          break;
+    case OPT_RESET:                 rc = reset(nargc,nargv);        break;
+    case OPT_GETOPERATORNAME:       rc = getoperatorname();         break;
+    case OPT_SETOPERATORNAME:       rc = setoperatorname(nargc,nargv);break;
+    case OPT_GETWAPBOOKMARK:        rc = getwapbookmark(nargc,nargv);break;
+    case OPT_SETWAPBOOKMARK:        rc = setwapbookmark(nargc,nargv);break;
+    case OPT_SAVEWAPBOOKMARK:       rc = savewapbookmark(nargc,nargv);break;
+    case OPT_SENDWAPBOOKMARK:       rc = sendwapbookmark(nargc,nargv);break;
+    case OPT_GETWAPSETTINGS:        rc = getwapsettings(nargc,nargv);break;
+    case OPT_SAVEWAPSETTINGS:       rc = savewapsettings(nargc,nargv);break;
+    case OPT_SENDWAPSETTINGS:       rc = sendwapsettings(nargc,nargv);break;
+    case OPT_ALLRINGTONES:          rc = allringtones();            break;
+    case OPT_GETPHONEPROFILE:       rc = getphoneprofile();         break;
+    case OPT_SETPHONEPROFILE:       rc = setphoneprofile(nargc,nargv);break;
+    case OPT_GETVOICEMAILBOX:       rc = getvoicemailbox();         break;
+    case OPT_NM_COLLECT:            rc = nm_collect(nargc, nargv);  break;
+    case OPT_NETMONITORDATA:        rc = netmonitordata(nargc, nargv);break;
+
+    default:         fprintf(stderr, _("Unknown option: %d\n"), c); break;
+
+    }
+
+    free(nargv);
+
+    return(rc);
+  }
+
+  fprintf(stderr, _("Wrong number of arguments\n"));
+
+  exit(-1);
+}
+
+/* Restores various phone settings from one file */
+int restoresettings(char *argv[])
+{
+  GSM_Backup Backup;
+  GSM_PhonebookEntry pbk;
+
+  int confirm;
+  char ans[4];
+
+  int i,pos;
+
+  GSM_MemoryStatus SIMMemoryStatus = {GMT_SM, 0, 0};
+  GSM_MemoryStatus PhoneMemoryStatus = {GMT_ME, 0, 0};
+
+  if (GSM_ReadBackupFile(argv[0], &Backup)!=GE_NONE) return 1;
+
+  fbusinit(NULL);
+
+  if (Backup.SIMPhonebookUsed!=0) {
+    confirm=-1;    
+    while (confirm < 0) {
+      fprintf(stderr, _("Restore SIM phonebook ? "));
+      GetLine(stdin, ans, 99);
+      if (!strcmp(ans, "yes")) confirm = 1;
+      if (!strcmp(ans, "no" )) confirm = 0;
+    }
+    if (confirm==1) {
+      if (GSM->GetMemoryStatus(&SIMMemoryStatus) != GE_NONE) {
+        fprintf(stderr,_("Error getting memory status !\n"));
+        GSM->Terminate();
+      }
+      i=0;pos=1;
+
+      while ((pos-1)!=SIMMemoryStatus.Used+SIMMemoryStatus.Free) {
+        pbk.Location=pos;
+        pbk.MemoryType=GMT_SM;
+        pbk.Name[0]=0;
+        pbk.Number[0]=0;
+        pbk.SubEntriesCount = 0;
+        if (i<Backup.SIMPhonebookUsed) {
+          if (Backup.SIMPhonebook[i].Location==pbk.Location) {
+            pbk=Backup.SIMPhonebook[i];
+            i++;
+//#ifdef DEBUG
+//            fprintf(stdout,_("Copying from backup\n"));
+//#endif
+          }
+        }
+//#ifdef DEBUG
+//        fprintf(stdout,_("Setting location %i\n"),pbk.Location);
+//#endif
+        GSM->WritePhonebookLocation(&pbk);
+       fprintf(stderr,_("."));
+        pos++;
+      }
+      fprintf(stderr,_("\n"));
+    }
+  }
+  if (Backup.PhonePhonebookUsed!=0) {
+    confirm=-1;    
+    while (confirm < 0) {
+      fprintf(stderr, _("Restore phone phonebook ? "));
+      GetLine(stdin, ans, 99);
+      if (!strcmp(ans, "yes")) confirm = 1;
+      if (!strcmp(ans, "no" )) confirm = 0;
+    }
+    if (confirm==1) {
+      if (GSM->GetMemoryStatus(&PhoneMemoryStatus) != GE_NONE) {
+        fprintf(stderr,_("Error getting memory status !\n"));
+        GSM->Terminate();
+      }
+
+      i=0;pos=1;
+
+      while ((pos-1)!=PhoneMemoryStatus.Used+PhoneMemoryStatus.Free) {
+        pbk.Location=pos;
+        pbk.MemoryType=GMT_ME;
+        pbk.Name[0]=0;
+        pbk.Number[0]=0;
+        pbk.SubEntriesCount = 0;
+        if (i<Backup.PhonePhonebookUsed) {
+          if (Backup.PhonePhonebook[i].Location==pbk.Location) {
+            pbk=Backup.PhonePhonebook[i];
+            i++;
+//#ifdef DEBUG
+//            fprintf(stdout,_("Copying from backup\n"));
+//#endif
+          }
+        }
+//#ifdef DEBUG
+//        fprintf(stdout,_("Setting location %i\n"),pbk.Location);
+//#endif
+        GSM->WritePhonebookLocation(&pbk);
+       fprintf(stderr,_("."));
+        pos++;
+      }
+      fprintf(stderr,_("\n"));
+    }
+  }
+  if (Backup.CallerAvailable==true) {
+    confirm=-1;    
+    while (confirm < 0) {
+      fprintf(stderr, _("Restore caller groups ? "));
+      GetLine(stdin, ans, 99);
+      if (!strcmp(ans, "yes")) confirm = 1;
+      if (!strcmp(ans, "no" )) confirm = 0;
+    }
+    if (confirm==1) {
+      for (i=0;i<5;i++) GSM->SetBitmap(&Backup.CallerGroups[i]);
+    }
+  }
+  if (Backup.OperatorLogoAvailable==true) {
+    confirm=-1;    
+    while (confirm < 0) {
+      fprintf(stderr, _("Restore operator logo ? "));
+      GetLine(stdin, ans, 99);
+      if (!strcmp(ans, "yes")) confirm = 1;
+      if (!strcmp(ans, "no" )) confirm = 0;
+    }
+    if (confirm==1) {
+      GSM->SetBitmap(&Backup.OperatorLogo);
+    }
+  }
+  if (Backup.StartupLogoAvailable==true) {
+    confirm=-1;    
+    while (confirm < 0) {
+      fprintf(stderr, _("Restore startup logo ? "));
+      GetLine(stdin, ans, 99);
+      if (!strcmp(ans, "yes")) confirm = 1;
+      if (!strcmp(ans, "no" )) confirm = 0;
+    }
+    if (confirm==1) {
+      GSM->SetBitmap(&Backup.StartupLogo);
+    }
+  }
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+/* Backup various phone settings from one file */
+int backupsettings(char *argv[])
+{
+  GSM_PhonebookEntry PbkEntry;
+  GSM_Error error;
+  GSM_Backup Backup;
+  int i;
+
+  GSM_MemoryStatus SIMMemoryStatus = {GMT_SM, 0, 0};
+  GSM_MemoryStatus PhoneMemoryStatus = {GMT_ME, 0, 0};
+
+  fbusinit(NULL);
+
+  fprintf(stderr,_("Backup phonebook from SIM..."));
+  Backup.SIMPhonebookUsed=0;
+  if (GSM->GetMemoryStatus(&SIMMemoryStatus) == GE_NONE) {
+    Backup.SIMPhonebookSize=SIMMemoryStatus.Used+SIMMemoryStatus.Free;
+
+    PbkEntry.MemoryType=GMT_SM;
+
+    for (i=0;i<Backup.SIMPhonebookSize;i++)
+    {
+      if (SIMMemoryStatus.Used==Backup.SIMPhonebookUsed) break;
+
+      PbkEntry.Location=i;
+    
+      error=GSM->GetMemoryLocation(&PbkEntry);
+      switch (error) {
+        case GE_NONE:
+          Backup.SIMPhonebook[Backup.SIMPhonebookUsed]=PbkEntry;
+          Backup.SIMPhonebookUsed++;
+          fprintf(stderr,_("."));
+          break;
+        default:
+          break;
+      }
+    }
+    fprintf(stderr,_("Done\n"));
+  } else fprintf(stderr,_("ERROR\n"));
+
+  fprintf(stderr,_("Backup phonebook from phone..."));
+  Backup.PhonePhonebookUsed=0;
+  if (GSM->GetMemoryStatus(&PhoneMemoryStatus) == GE_NONE) {
+    Backup.PhonePhonebookSize=PhoneMemoryStatus.Used+PhoneMemoryStatus.Free;
+
+    PbkEntry.MemoryType=GMT_ME;
+
+    for (i=0;i<Backup.PhonePhonebookSize;i++)
+    {
+      if (PhoneMemoryStatus.Used==Backup.PhonePhonebookUsed) break;
+
+      PbkEntry.Location=i;
+    
+      error=GSM->GetMemoryLocation(&PbkEntry);
+      switch (error) {
+        case GE_NONE:
+          Backup.PhonePhonebook[Backup.PhonePhonebookUsed]=PbkEntry;
+          Backup.PhonePhonebookUsed++;
+          fprintf(stderr,_("."));
+          break;
+        default:
+          break;
+      }
+    }
+    fprintf(stderr,_("Done\n"));
+  } else fprintf(stderr,_("ERROR\n"));
+
+  if( GetModelFeature (FN_CALLERGROUPS)!=0) {
+    fprintf(stderr,_("Backup caller logos..."));
+    Backup.CallerAvailable=true;
+    for (i=0;i<5;i++) {
+      Backup.CallerGroups[i].number=i;
+      Backup.CallerGroups[i].type=GSM_CallerLogo;
+      if (GSM->GetBitmap(&Backup.CallerGroups[i])!=GE_NONE) return 1;
+    }
+    fprintf(stderr,_("Done\n"));
+  } else Backup.CallerAvailable=false;
+
+//  fprintf(stderr,_("Backup speed dials..."));
+  Backup.SpeedAvailable=false;
+//  for (i=0;i<8;i++) {
+//    Backup.SpeedDials[i].Number=i+1;
+//    if (GSM->GetSpeedDial(&Backup.SpeedDials[i])!=GE_NONE) return 1;
+//  }
+//  fprintf(stderr,_("Done\n"));
+
+  fprintf(stderr,_("Backup operator logo..."));
+  Backup.OperatorLogoAvailable=true;
+  Backup.OperatorLogo.type=GSM_7110OperatorLogo;
+  if (GSM->GetBitmap(&Backup.OperatorLogo)!=GE_NONE) {
+    Backup.OperatorLogo.type=GSM_OperatorLogo;
+    if (GSM->GetBitmap(&Backup.OperatorLogo)!=GE_NONE) {
+      Backup.OperatorLogoAvailable=false;
+     fprintf(stderr,_("Error\n"));
+    } else fprintf(stderr,_("Done\n"));
+  } else fprintf(stderr,_("Done\n"));
+
+  Backup.StartupLogoAvailable=false;
+  if( GetModelFeature (FN_STARTUP)!=0) {
+    fprintf(stderr,_("Backup startup logo..."));
+    Backup.StartupLogoAvailable=true;
+    switch (GetModelFeature (FN_STARTUP)) {
+      case F_STA62: Backup.StartupLogo.type=GSM_6210StartupLogo;break;
+      case F_STA71: Backup.StartupLogo.type=GSM_7110StartupLogo;break;
+      default     : Backup.StartupLogo.type=GSM_StartupLogo;break;
+    }
+    if (GSM->GetBitmap(&Backup.StartupLogo)!=GE_NONE) {
+      Backup.StartupLogoAvailable=false;
+      fprintf(stderr,_("Error\n"));
+    } else fprintf(stderr,_("Done\n"));
+  }
+
+  fprintf(stderr,_("Backup welcome note..."));
+  Backup.StartupText.type=GSM_WelcomeNoteText;
+  if (GSM->GetBitmap(&Backup.StartupText)!=GE_NONE) {
+    fprintf(stderr,_("Error\n"));
+  } else fprintf(stderr,_("Done\n"));
+
+  GSM->Terminate();
+
+  GSM_SaveBackupFile(argv[0], &Backup);
+
+  return 0;
+}
+
+/* Presses keys on phone's keyboard */
+
+int presskeysequence(char *argv[])
+{
+  int i,j;
+  int keycode;
+  char key;
+  
+  sleep(1);
+
+  fbusinit(NULL);
+  
+  for (i=0;i<strlen(argv[0]);i++)
+  {
+    key=argv[0][i];
+    keycode=0;
+    j=0;
+    
+    if (key!='w' && key!='W')
+    {
+      while (Keys[j].whatchar!=' ') {
+        if (Keys[j].whatchar==key) {    
+          keycode=Keys[j].whatcode;
+          break;
+        }
+        j++;
+      }
+    
+      if (keycode==0) {
+        fprintf(stderr,_("Unknown key: %c !\n"),key);
+        GSM->Terminate();
+        return -1;
+      }
+    
+      if (GSM->PressKey(keycode,PRESSPHONEKEY)!=GE_NONE)
+      {
+        fprintf(stderr,_("Can't press key !\n"));
+        GSM->Terminate();
+        return -1;
+      }
+      if (GSM->PressKey(keycode,RELEASEPHONEKEY)!=GE_NONE)
+      {
+        fprintf(stderr,_("Can't release key !\n"));
+        GSM->Terminate();
+        return -1;
+      }
+    } else
+    {
+      sleep(2);
+    }
+  }
+  
+  GSM->Terminate();
+
+  return 0;
+}
+
+/* Send  SMS messages. */
+int sendsms(int argc, char *argv[])
+{
+  GSM_MultiSMSMessage MultiSMS;
+  char message_buffer[GSM_MAX_CONCATENATED_SMS_LENGTH];
+  int input_len, chars_read,i,msgnum;
+
+  GSM_SMSMessageType SMSType=GST_SMS;
+  int SMSValidity= 4320; /* 4320 minutes == 72 hours */
+  bool SMSReply=false;
+  int SMSClass=-1,SMSCenter=1;
+  char SMSCNumber[100];
+  GSM_Coding_Type SMSCoding=GSM_Coding_Default;
+  GSM_UDH SMSUDHType=GSM_NoUDH;
+
+  struct option options[] = {
+             { "smscno",       required_argument, NULL, '1'},
+             { "smsc",         required_argument, NULL, '2'},
+             { "long",        required_argument, NULL, '3'},
+             { "enablevoice",  no_argument,       NULL, '4'},
+            { "disablevoice", no_argument,       NULL, '5'},
+            { "enableemail",  no_argument,       NULL, '6'},
+            { "disableemail", no_argument,       NULL, '7'},
+            { "enablefax",    no_argument,       NULL, '8'},
+            { "disablefax",   no_argument,       NULL, '9'},
+            { "unicode",      no_argument,       NULL, '-'},
+            { "void",         no_argument,       NULL, '+'},
+            { "hang",         no_argument,       NULL, '('},
+            { "bug",          no_argument,       NULL, ')'},
+             { NULL,           0,                 NULL, 0}
+  };
+  
+  input_len = GSM_MAX_SMS_LENGTH;
+
+  if (argc!=0) {
+
+    optarg = NULL;
+    optind = 0;
+
+    while ((i = getopt_long(argc, argv, "v:dsC:", options, NULL)) != -1) {
+      switch (i) {
+
+        case '1': /* SMSC number */
+          SMSCenter = 0;
+          strcpy(SMSCNumber,optarg);
+          break;
+
+        case '2': /* SMSC number index in phone memory */
+          SMSCenter = atoi(optarg);
+
+          if (SMSCenter < 1 || SMSCenter > 5) {
+            fprintf(stderr, _("Incorrect SMSC number with \"smscno\" option (can't be <1 and >5) !\n"));
+            GSM->Terminate();
+            return -1;
+         }
+          break;
+
+        case '3': /* we send long message */
+          SMSUDHType=GSM_ConcatenatedMessages;
+          input_len = atoi(optarg);
+          if (input_len > GSM_MAX_CONCATENATED_SMS_LENGTH) {
+           fprintf(stderr, _("Input too long, max %i!\n"),GSM_MAX_CONCATENATED_SMS_LENGTH);
+           exit(-1);
+          }
+          break;
+
+        case '4': /* SMS enables voice indicator */
+          SMSUDHType=GSM_EnableVoice;    break;
+
+        case '5': /* SMS disables voice indicator */
+          SMSUDHType=GSM_DisableVoice;   break;          
+
+        case '6': /* SMS enables email indicator */
+          SMSUDHType=GSM_EnableEmail;    break;          
+
+        case '7': /* SMS disables email indicator */
+          SMSUDHType=GSM_DisableEmail;   break;          
+
+        case '8': /* SMS enables fax indicator */
+          SMSUDHType=GSM_EnableFax;      break;          
+
+        case '9': /* SMS disables fax indicator */
+          SMSUDHType=GSM_DisableFax;     break;          
+
+        case '-': /* SMS coding type */
+          SMSCoding=GSM_Coding_Unicode;  break;
+
+        case '+': /* SMS ghost */
+          SMSUDHType=GSM_VoidSMS;        break;
+
+        case '(': /* SMS hanging phone, when saved to Outbox */
+          SMSUDHType=GSM_HangSMS;        break;
+
+        case ')': /* SMS showed incorrectly in phone */
+          SMSUDHType=GSM_BugSMS;         break;
+
+        case 'v': /* Set validaty of SMS */
+          SMSValidity = atoi(optarg);    break;
+
+        case 'd': /* delivery report */
+          SMSType=GST_DR;                break;        
+
+        case 's': /* Set replying via the same SMSC */
+          SMSReply = true;               break;
+
+        case 'C': /* class Message */
+          
+         if (SMSUDHType!=GSM_NoUDH) {
+            fprintf(stderr, _("Can't specify SMS Class with --enablevoice, --disablevoice, --enableemail, --disableemail, --enablefax, --disablefax options !\n"));        
+           return -1;
+         }
+         
+          switch (*optarg) {
+            case '0': SMSClass = 0; break;
+            case '1': SMSClass = 1; break;
+            case '2': SMSClass = 2; break;
+            case '3': SMSClass = 3; break; 
+            default:
+             fprintf(stderr, _("SMS Class (\"C\" option) can be 0, 1, 2 or 3 only !\n"));
+              return -1;
+          }
+          break;
+         
+        default:
+          fprintf(stderr,_("Unknown option number %i\n"),argc);
+          return -1;
+      }
+    }
+  }
+  
+  /* Get message text from stdin. */
+  chars_read = fread(message_buffer, 1, input_len, stdin);
+
+  if (chars_read == 0) {
+    fprintf(stderr, _("Couldn't read from stdin!\n")); 
+    return -1;
+  }
+  if (chars_read > input_len) {
+    fprintf(stderr, _("Input too long!\n"));   
+    return -1;
+  }
+  
+  /*  Null terminate. */
+  message_buffer[chars_read] = 0x00;   
+
+  GSM_MakeMultiPartSMS2(&MultiSMS,message_buffer,chars_read,SMSUDHType,SMSCoding);
+  msgnum=MultiSMS.number;
+
+  switch (SMSUDHType) {
+    case GSM_NoUDH:
+    case GSM_BugSMS:
+    case GSM_VoidSMS:
+    case GSM_HangSMS:
+    case GSM_EnableVoice:
+    case GSM_DisableVoice:
+    case GSM_EnableFax:
+    case GSM_DisableFax:
+    case GSM_EnableEmail:
+    case GSM_DisableEmail:
+      fprintf(stdout,_("Warning: saving %i chars\n"),strlen(MultiSMS.SMS[0].MessageText));
+      msgnum=1;
+      break;
+    default:
+      break;
+  }
+
+  for (i=0;i<msgnum;i++) {
+    strcpy(MultiSMS.SMS[i].Destination,argv[0]);
+
+    MultiSMS.SMS[i].Class=SMSClass;
+    MultiSMS.SMS[i].ReplyViaSameSMSC=SMSReply;
+    MultiSMS.SMS[i].Type=SMSType;
+    MultiSMS.SMS[i].Validity=SMSValidity;
+  }
+
+  /* Initialise the GSM interface. */     
+  fbusinit(NULL);
+
+  MultiSMS.number=msgnum;
+  GSM_SendMultiPartSMSOnConsole(&MultiSMS, 0,0,NULL,false,false,false);
+  
+  return 0;
+}
+
+int savesms(int argc, char *argv[])
+{
+  GSM_MultiSMSMessage MultiSMS;
+  char message_buffer[GSM_MAX_CONCATENATED_SMS_LENGTH];
+  int input_len, chars_read,i,msgnum;
+
+  int SMSClass=-1,SMSCenter=1;
+  char SMSName[25+1];
+  char SMSCNumber[100];
+  GSM_Coding_Type SMSCoding=GSM_Coding_Default;
+  GSM_UDH SMSUDHType=GSM_NoUDH;
+  GSM_SMSMessageStatus SMSStatus;
+  int SMSFolder;
+  bool SMSReply=false;
+  int SMSLocation=0;
+  bool interactive=false;
+
+  struct option options[] = {
+             { "smscno",       required_argument, NULL, '1'},
+             { "smsc",         required_argument, NULL, '2'},
+             { "long",        required_argument, NULL, '3'},
+             { "enablevoice",  no_argument,       NULL, '4'},
+            { "disablevoice", no_argument,       NULL, '5'},
+            { "enableemail",  no_argument,       NULL, '6'},
+            { "disableemail", no_argument,       NULL, '7'},
+            { "enablefax",    no_argument,       NULL, '8'},
+            { "disablefax",   no_argument,       NULL, '9'},
+            { "unicode",      no_argument,       NULL, '-'},
+            { "void",         no_argument,       NULL, '+'},
+            { "hang",         no_argument,       NULL, '('},
+            { "bug",          no_argument,       NULL, ')'},
+            { "smsname",      required_argument, NULL, '/'},
+             { NULL,           0,                 NULL, 0}
+  };
+
+  SMSCNumber[0]=0;
+  SMSName[0]=0;
+  SMSStatus=GSS_NOTSENTREAD;
+  SMSFolder=GST_OUTBOX;
+  
+  input_len = GSM_MAX_SMS_LENGTH;
+
+  if (argc!=0) {
+
+    optarg = NULL;
+    optind = 0;
+
+    while ((i = getopt_long(argc, argv, "risal:C:F:", options, NULL)) != -1) {
+      switch (i) {
+
+        case '1': /* SMSC number */
+          SMSCenter = 0;
+          strcpy(SMSCNumber,optarg);
+          break;
+
+        case '2': /* SMSC number index in phone memory */
+          SMSCenter = atoi(optarg);
+
+          if (SMSCenter < 1 || SMSCenter > 5) {
+            fprintf(stderr, _("Incorrect SMSC number with \"smscno\" option (can't be <1 and >5) !\n"));
+            GSM->Terminate();
+            return -1;
+         }
+          break;
+
+        case '3': /* we send long message */
+          SMSUDHType=GSM_ConcatenatedMessages;
+          input_len = atoi(optarg);
+          if (input_len > GSM_MAX_CONCATENATED_SMS_LENGTH) {
+           fprintf(stderr, _("Input too long, max %i!\n"),GSM_MAX_CONCATENATED_SMS_LENGTH);
+           exit(-1);
+          }
+          break;
+
+        case '4': /* SMS enables voice indicator */
+          SMSUDHType=GSM_EnableVoice;    break;
+
+        case '5': /* SMS disables voice indicator */
+          SMSUDHType=GSM_DisableVoice;   break;          
+
+        case '6': /* SMS enables email indicator */
+          SMSUDHType=GSM_EnableEmail;    break;          
+
+        case '7': /* SMS disables email indicator */
+          SMSUDHType=GSM_DisableEmail;   break;          
+
+        case '8': /* SMS enables fax indicator */
+          SMSUDHType=GSM_EnableFax;      break;          
+
+        case '9': /* SMS disables fax indicator */
+          SMSUDHType=GSM_DisableFax;     break;          
+
+        case '-': /* SMS coding type */
+          SMSCoding=GSM_Coding_Unicode;  break;
+
+        case '+': /* SMS ghost */
+          SMSUDHType=GSM_VoidSMS;        break;
+
+        case '(': /* SMS hanging phone, when saved to Outbox */
+          SMSUDHType=GSM_HangSMS;        break;
+
+        case ')': /* SMS showed incorrectly in phone */
+          SMSUDHType=GSM_BugSMS;         break;
+
+        case 'r': /* mark as read */
+          SMSStatus = GSS_SENTREAD; break;
+        case 'i': /* Save into Inbox */
+          SMSFolder = GST_INBOX; break;
+         
+        case 's': /* Set replying via the same SMSC */
+          SMSReply = true; break;
+
+        case 'a': /* Ask before overwriting */
+          interactive=true;break;     
+       
+        case 'l': /* Specify location */
+          SMSLocation = atoi(optarg); break;     
+
+        case '/': /* Name */
+          strncpy(SMSName,optarg,25);break;
+
+        case 'C': /* class Message */
+          
+         if (SMSUDHType!=GSM_NoUDH) {
+            fprintf(stderr, _("Can't specify SMS Class with --enablevoice, --disablevoice, --enableemail, --disableemail, --enablefax, --disablefax options !\n"));        
+           return -1;
+         }
+         
+          switch (*optarg) {
+            case '0': SMSClass = 0; break;
+            case '1': SMSClass = 1; break;
+            case '2': SMSClass = 2; break;
+            case '3': SMSClass = 3; break; 
+            default:
+             fprintf(stderr, _("SMS Class (\"C\" option) can be 0, 1, 2 or 3 only !\n"));
+              return -1;
+          }
+          break;
+
+        case 'F': /* save into folder n */
+         SMSFolder = atoi(optarg);
+          break;
+
+        default:
+          fprintf(stderr,_("Unknown option number %i\n"),argc);
+          return -1;
+      }
+    }
+  }
+  
+  /* Get message text from stdin. */
+  chars_read = fread(message_buffer, 1, input_len, stdin);
+
+  if (chars_read == 0) {
+    fprintf(stderr, _("Couldn't read from stdin!\n")); 
+    return -1;
+  }
+  if (chars_read > input_len) {
+    fprintf(stderr, _("Input too long!\n"));   
+    return -1;
+  }
+  
+  /*  Null terminate. */
+  message_buffer[chars_read] = 0x00;   
+
+  GSM_MakeMultiPartSMS2(&MultiSMS,message_buffer,chars_read,SMSUDHType,SMSCoding);
+  msgnum=MultiSMS.number;
+
+  switch (SMSUDHType) {
+    case GSM_NoUDH:
+    case GSM_BugSMS:
+    case GSM_VoidSMS:
+    case GSM_HangSMS:
+    case GSM_EnableVoice:
+    case GSM_DisableVoice:
+    case GSM_EnableFax:
+    case GSM_DisableFax:
+    case GSM_EnableEmail:
+    case GSM_DisableEmail:
+      fprintf(stdout,_("Warning: saving %i chars\n"),strlen(MultiSMS.SMS[0].MessageText));
+      msgnum=1;
+      break;
+    default:
+      break;
+  }
+
+  for (i=0;i<msgnum;i++) {
+    MultiSMS.SMS[i].Destination[0]=0;
+    if (argc!=0) strcpy(MultiSMS.SMS[i].Destination,argv[0]);
+
+    MultiSMS.SMS[i].Location=0;
+    MultiSMS.SMS[i].Class=SMSClass;
+    MultiSMS.SMS[i].MessageCenter.No=SMSCenter;
+    strcpy(MultiSMS.SMS[i].MessageCenter.Number,SMSCNumber);
+    MultiSMS.SMS[i].Status=SMSStatus;
+    strcpy(MultiSMS.SMS[i].Name,SMSName);
+    MultiSMS.SMS[i].folder=SMSFolder;
+    MultiSMS.SMS[i].ReplyViaSameSMSC=SMSReply;
+  }
+
+  MultiSMS.SMS[0].Location=SMSLocation;
+
+  /* Initialise the GSM interface. */     
+  fbusinit(NULL);
+
+  MultiSMS.number=msgnum;
+  GSM_SaveMultiPartSMSOnConsole(&MultiSMS, 0,0,NULL,interactive,false,false,false);
+  
+  return 0;
+}
+
+/* Get SMSC number */
+
+int getsmsc(char *MessageCenterNumber)
+{
+
+  GSM_MessageCenter MessageCenter;
+
+  MessageCenter.No=atoi(MessageCenterNumber);
+
+  fbusinit(NULL);
+
+  if (GSM->GetSMSCenter(&MessageCenter) == GE_NONE) {
+
+    fprintf(stdout, _("%d. SMS center ("),MessageCenter.No);
+    
+    if (!strcmp(MessageCenter.Name,""))
+      fprintf(stdout,_("Set %d"),MessageCenter.No);
+    else fprintf(stdout,_("%s"),MessageCenter.Name);
+      
+    fprintf(stdout,_(") number is "));
+
+    if (!strcmp(MessageCenter.Number,"")) fprintf(stdout,_("not set\n"));
+    else fprintf(stdout,_("%s\n"),MessageCenter.Number);
+
+    fprintf(stdout,_("Default recipient number is "));
+
+    if (!strcmp(MessageCenter.DefaultRecipient,""))
+      fprintf(stdout,_("not set\n"));
+    else fprintf(stdout,_("%s\n"),MessageCenter.DefaultRecipient);
+
+    fprintf(stdout, _("Messages sent as "));
+
+    switch (MessageCenter.Format) {
+      case GSMF_Text  :fprintf(stdout, _("Text"));break;
+      case GSMF_Paging:fprintf(stdout, _("Paging"));break;
+      case GSMF_Fax   :fprintf(stdout, _("Fax"));break;
+      case GSMF_Email :
+      case GSMF_UCI   :fprintf(stdout, _("Email"));break;
+      case GSMF_ERMES :fprintf(stdout, _("ERMES"));break;
+      case GSMF_X400  :fprintf(stdout, _("X.400"));break;
+      default         :fprintf(stdout, _("Unknown"));
+    }
+
+    printf("\n");
+
+    fprintf(stdout, _("Message validity is "));
+
+    switch (MessageCenter.Validity) {
+      case GSMV_1_Hour  :fprintf(stdout, _("1 hour"));break;
+      case GSMV_6_Hours :fprintf(stdout, _("6 hours"));break;
+      case GSMV_24_Hours:fprintf(stdout, _("24 hours"));break;
+      case GSMV_72_Hours:fprintf(stdout, _("72 hours"));break;
+      case GSMV_1_Week  :fprintf(stdout, _("1 week"));break;
+      case GSMV_Max_Time:fprintf(stdout, _("Maximum time"));break;
+      default           :fprintf(stdout, _("Unknown"));
+    }
+
+    fprintf(stdout, "\n");
+
+  }
+  else
+    fprintf(stdout, _("SMS center can not be found :-(\n"));
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+/* Get SMS messages. */
+int getsms(int argc, char *argv[])
+{
+
+  GSM_SMSMessage message;
+  GSM_WAPBookmark bookmark;
+  char memory_type_string[20];
+  int start_message, end_message, count, mode = 1;
+  char filename[64];
+  GSM_Error error;
+  GSM_Bitmap bitmap;
+  GSM_Ringtone ringtone;
+  GSM_SMSFolders folders;
+  
+  int confirm = -1, i;
+  char ans[8];
+
+  /* Handle command line args that set type, start and end locations. */
+  if (!GetMemoryTypeID(argv[2], &message.MemoryType))
+  {
+    fprintf(stderr, _("Unknown memory type %s!\n"), argv[2]);
+    return (-1);
+  }
+  GetMemoryTypeString(memory_type_string, &message.MemoryType);
+
+  for (i=0;i<64;i++) filename[i]=0;
+
+  start_message = atoi(argv[3]);
+  if (argc > 4) {
+     int i;
+     
+     /* [end] can be only argv[4] */
+     if (argv[4][0] == '-') { end_message = start_message; }
+                       else { end_message = atoi(argv[4]); }
+
+     /* parse all options (beginning with '-' */
+     while ((i = getopt(argc, argv, "f:")) != -1) {
+       switch (i) {
+         case 'f':
+           if (optarg) {
+#ifdef DEBUG
+          fprintf(stderr, _("Saving into file \"%s\"\n"), optarg);
+#endif /* DEBUG */
+             strncpy(filename, optarg, 64);
+              if (strlen(optarg) > 63) {
+                fprintf(stderr, _("Filename too long - will be truncated to 63 characters.\n"));
+                filename[63] = 0;
+              } else {
+                filename[strlen(optarg)] = 0;
+              }
+           } else {
+             usage();
+             exit(1);
+           }
+           break;
+         default:
+           usage();
+           exit(1);
+       }
+      }
+  } else {
+    end_message = start_message;
+  }
+
+  /* Initialise the code for the GSM interface. */     
+
+  fbusinit(NULL);
+
+  GSM->GetSMSFolders(&folders);
+
+  
+  /* Now retrieve the requested entries. */
+
+  for (count = start_message; count <= end_message; count ++) {
+
+    message.Location = count;
+
+    error = GSM->GetSMSMessage(&message);
+
+    switch (error) {
+
+    case GE_NONE:
+
+      switch (message.Type) {
+
+        case GST_DR:
+
+         /* RTH FIXME: Test that out ! */
+          fprintf(stdout, _("%d. Delivery Report "), message.MessageNumber);
+          switch (message.Status)
+          {
+           case  GSS_SENTREAD:
+                if (message.folder==0) fprintf(stdout, _("(read)\n")); //GST_INBOX
+                                  else fprintf(stdout, _("(sent)\n"));
+               break;
+           case  GSS_NOTSENTREAD:
+                if (message.folder==0) fprintf(stdout, _("(unread)\n")); //GST_INBOX
+                                  else fprintf(stdout, _("(not sent)\n"));
+               break;
+           case  GSS_UNKNOWN:
+               fprintf(stdout, _("(not known :-()\n"));
+               break;
+           case  GSS_TEMPLATE:
+               fprintf(stdout, _("(template)\n"));
+               break;
+           default:
+               fprintf(stdout, _("(unknown: %d)\n"),message.Status);
+               break;
+          }
+
+          fprintf(stdout, _("Sending date/time : %s %02d/%02d/%02d %d:%02d:%02d "), \
+                  DayOfWeek(message.Time.Year, message.Time.Month, message.Time.Day), \
+                 message.Time.Day, message.Time.Month, message.Time.Year, \
+                  message.Time.Hour, message.Time.Minute, message.Time.Second);
+
+          if (message.Time.Timezone) {
+            if (message.Time.Timezone > 0)
+              fprintf(stdout,_("+%02d00"), message.Time.Timezone);
+            else
+              fprintf(stdout,_("%02d00"), message.Time.Timezone);
+          }
+
+          fprintf(stdout, "\n");
+
+          fprintf(stdout, _("Response date/time: %s %02d/%02d/%02d %d:%02d:%02d "), \
+                  DayOfWeek(message.SMSCTime.Year, message.SMSCTime.Month, message.SMSCTime.Day), \
+                  message.SMSCTime.Day, message.SMSCTime.Month, message.SMSCTime.Year, \
+                  message.SMSCTime.Hour, message.SMSCTime.Minute, message.SMSCTime.Second);
+
+          if (message.SMSCTime.Timezone) {
+            if (message.SMSCTime.Timezone > 0)
+              fprintf(stdout,_("+%02d00"),message.SMSCTime.Timezone);
+            else
+              fprintf(stdout,_("%02d00"),message.SMSCTime.Timezone);
+          }
+
+          fprintf(stdout, "\n");
+
+          fprintf(stdout, _("Receiver: %s Msg Center: %s\n"), message.Sender, message.MessageCenter.Number);
+          fprintf(stdout, _("Text: %s\n\n"), message.MessageText); 
+
+          break;
+
+        case GST_SMS:
+          fprintf(stdout, _("%d. %s Message "), message.MessageNumber,
+                          folders.Folder[message.folder].Name);
+
+         switch (message.Status)
+           {
+           case  GSS_SENTREAD:
+                if (message.folder==0) fprintf(stdout, _("(read)\n")); //GST_INBOX
+                                  else fprintf(stdout, _("(sent)\n"));
+               break;
+           case  GSS_NOTSENTREAD:
+                if (message.folder==0) fprintf(stdout, _("(unread)\n")); //GST_INBOX
+                                  else fprintf(stdout, _("(not sent)\n"));
+               break;
+            case  GSS_UNKNOWN:
+                fprintf(stdout, _("(not known :-()\n"));
+                break;
+            case  GSS_TEMPLATE:
+                fprintf(stdout, _("(template)\n"));
+                break;
+            default:
+                fprintf(stdout, _("(unknown: %d)\n"),message.Status);
+                break;
+           }
+        
+         /* RTH FIXME: date for other status ok ? */ 
+         if (message.SMSData) {
+
+            fprintf(stdout, _("Date/time: %s %02d/%02d/%02d %d:%02d:%02d "), \
+                    DayOfWeek(message.Time.Year, message.Time.Month, message.Time.Day), \
+                    message.Time.Day, message.Time.Month, message.Time.Year, \
+                    message.Time.Hour, message.Time.Minute, message.Time.Second);
+
+            if (message.Time.Timezone) {
+              if (message.Time.Timezone > 0)
+                fprintf(stdout,_("+%02d00"),message.Time.Timezone);
+              else
+                fprintf(stdout,_("%02d00"),message.Time.Timezone);
+            }
+
+            fprintf(stdout, "\n");
+
+           fprintf(stdout, _("Msg Center: %s "), message.MessageCenter.Number);
+           
+           if (message.ReplyViaSameSMSC)
+             fprintf(stdout, _("(centre set for reply) "));
+         }
+
+          if (strcmp(message.Sender,"")) {
+           if (message.folder==1) { //GST_OUTBOX
+             fprintf(stdout, _("Recipient: %s"),message.Sender);
+           } else {
+             fprintf(stdout, _("Sender: %s"),message.Sender);
+           }
+         }
+
+          if (strcmp(message.Sender,"") || message.folder==0)
+            fprintf(stdout, "\n");
+           
+          switch (message.UDHType) {
+
+          case GSM_OpLogo:
+
+            /* put bitmap into bitmap structure */
+           switch (GSM_ReadBitmap(&message, &bitmap)) {
+             case GE_INVALIDIMAGESIZE:
+               fprintf(stdout,_("Image size not supported\n"));
+               break;
+             case GE_NONE:
+               fprintf(stdout, _("GSM operator logo for %s (%s) network.\n"), bitmap.netcode, GSM_GetNetworkName(bitmap.netcode));         
+           
+                GSM_PrintBitmap(&bitmap);
+
+                if (filename[0]!=0) {
+                  GSM_SaveBitmapFileOnConsole(filename, &bitmap);
+                }
+
+               break;
+             default:
+               fprintf(stdout,_("Error reading image\n"));  
+               break;
+           }
+
+#ifdef DEBUG
+            if (message.folder==0) { //GST_INBOX
+              if (!strcmp(message.Sender, "+998000005") &&
+                 !strcmp(message.MessageCenter.Number, "+886935074443") &&
+                 message.Time.Day==27 &&
+                 message.Time.Month==7 &&
+                 message.Time.Year==99 &&
+                 message.Time.Hour==0 &&
+                 message.Time.Minute==10 &&
+                 message.Time.Second==48) fprintf(stdout, _("Saved by Logo Express\n"));
+
+              /* Is it changed in next versions ? Or what ? */
+              if (!strcmp(message.Sender, "+998000002") ||
+                  !strcmp(message.Sender, "+998000003") ||
+                 !strcmp(message.Sender, "+998000004")) fprintf(stdout, _("Saved by Operator Logo Uploader by Thomas Kessler\n"));
+            } else {
+              if (!strcmp(message.Sender, "+8861234567890") &&
+                 !strcmp(message.MessageCenter.Number, "+886935074443")) fprintf(stdout, _("Saved by Logo Express\n"));
+           }
+           if (!strncmp(message.Sender, "OpLogo",6) &&
+               strlen(message.Sender)==11)
+              fprintf(stdout, _("Saved by gnokii\n"));         
+#endif
+
+            break;
+
+          case GSM_WAPBookmarkUDH:
+
+            /* put bookmark into bookmark structure */
+           switch (GSM_ReadWAPBookmark(&message, &bookmark)) {
+             case GE_NONE:
+                fprintf(stdout, ("WAP Bookmark\n"));
+
+                fprintf(stdout,_("Address: \"%s\"\n"),bookmark.address);
+
+                if (bookmark.title[0]==0)
+                  fprintf(stdout,_("Title: \"%s\"\n"),bookmark.address);
+                else
+                  fprintf(stdout,_("Title: \"%s\"\n"),bookmark.title);
+
+               break;
+             default:
+               fprintf(stdout,_("Error reading WAP Bookmark\n"));  
+               break;
+           }
+
+#ifdef DEBUG
+           if (!strcmp(message.Sender, "WAPBookmark"))
+              fprintf(stdout, _("Saved by gnokii\n"));         
+#endif
+
+            break;
+
+          case GSM_CallerIDLogo:
+
+            /* put bitmap into bitmap structure */
+           switch (GSM_ReadBitmap(&message, &bitmap)) {
+             case GE_INVALIDIMAGESIZE:
+               fprintf(stdout,_("Image size not supported\n"));
+               break;
+             case GE_NONE:
+                fprintf(stdout, ("Caller Logo\n"));
+           
+                GSM_PrintBitmap(&bitmap);
+
+                if (filename[0]!=0) {
+                  GSM_SaveBitmapFileOnConsole(filename, &bitmap);
+                }
+
+               break;
+             default:
+               fprintf(stdout,_("Error reading image\n"));  
+               break;
+           }
+
+#ifdef DEBUG
+            if (message.folder==0) { //GST_INBOX
+              if (!strcmp(message.Sender, "+998000005") &&
+                 !strcmp(message.MessageCenter.Number, "+886935074443") &&
+                 message.Time.Day==27 &&
+                 message.Time.Month==7 &&
+                 message.Time.Year==99 &&
+                 message.Time.Hour==0 &&
+                 message.Time.Minute==10 &&
+                 message.Time.Second==48) fprintf(stdout, _("Saved by Logo Express\n"));
+            } else {
+              if (!strcmp(message.Sender, "+8861234567890") &&
+                 !strcmp(message.MessageCenter.Number, "+886935074443")) fprintf(stdout, _("Saved by Logo Express\n"));
+           }
+           if (!strcmp(message.Sender, "GroupLogo"))
+              fprintf(stdout, _("Saved by gnokii\n"));         
+#endif
+
+            break;
+
+          case GSM_ProfileUDH:
+               fprintf(stdout, ("Profile SMS, part %i/%i\n"),message.UDH[11],message.UDH[10]);
+               break;
+
+          case GSM_WAPBookmarkUDHLong:
+               fprintf(stdout, ("WAP Bookmark, part %i/%i\n"),message.UDH[11],message.UDH[10]);
+               break;
+
+          case GSM_WAPSettingsUDH:
+               fprintf(stdout, ("WAP Settings, part %i/%i\n"),message.UDH[11],message.UDH[10]);
+               break;
+         
+          case GSM_RingtoneUDH:
+
+            /* put ringtone into ringtone structure */
+            switch (GSM_ReadRingtone(&message, &ringtone)) {
+             case GE_NONE:
+
+               fprintf(stdout, ("Ringtone \"%s\"\n"),ringtone.name);
+
+                while (confirm < 0) {
+                  fprintf(stderr, _("Do you want to play it ? (yes/no) "));
+                  GetLine(stdin, ans, 7);
+                  if (!strcmp(ans, "yes")) confirm = 1;
+                  if (!strcmp(ans, "no")) confirm = 0;
+                }  
+
+                if (confirm==1) GSM_PlayRingtoneOnConsole(&ringtone);
+               
+                if (filename[0]!=0) GSM_SaveRingtoneFileOnConsole(filename, &ringtone);
+               
+               break;
+               
+              default:
+               fprintf(stdout,_("Gnokii can't read this ringtone - there is probably error inside\n"));
+               break;
+           }
+
+            break;
+
+          case GSM_CalendarNoteUDH:
+           fprintf(stdout, ("Calendar note SMS, part %i/%i\n"),message.UDH[11],message.UDH[10]);
+           fprintf(stdout, _("Text:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+           break;
+
+          case GSM_ConcatenatedMessages:
+            fprintf(stdout, _("Linked (%d/%d)\nText:\n%s\n\n"),message.UDH[5],message.UDH[4], message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+           break;
+
+          case GSM_EnableVoice:
+            fprintf(stdout, _("Enables voice indicator\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_DisableVoice:
+            fprintf(stdout, _("Disables voice indicator\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_EnableFax:
+            fprintf(stdout, _("Enables fax indicator\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_DisableFax:
+            fprintf(stdout, _("Disables fax indicator\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_EnableEmail:
+            fprintf(stdout, _("Enables email indicator\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_DisableEmail:
+            fprintf(stdout, _("Disables email indicator\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_VoidSMS:
+            fprintf(stdout, _("Void SMS\nText:\n%s\n\n"), message.MessageText);
+            if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+            break;
+
+          case GSM_NoUDH:
+           if (message.Coding!=GSM_Coding_8bit) {
+              fprintf(stdout, _("Text:\n%s\n\n"), message.MessageText);
+              if (filename[0]!=0 && mode != -1) mode = GSM_SaveTextFileOnConsole(filename, message.MessageText, mode);
+           } else {
+             fprintf(stdout, _("Message cannot be displayed here\n")); // like in phone :-)
+           }
+            break;
+
+          default:  //GSM_UnknownUDH and other
+            fprintf(stderr, _("Unknown\n"));
+          }
+
+          break;
+         
+       default:
+         fprintf(stdout,_("Unknown SMS type. Report it\n"));
+         break;
+      }
+
+      break;
+
+    case GE_NOTIMPLEMENTED:
+
+      fprintf(stderr, _("Function not implemented in %s model!\n"), model);
+      GSM->Terminate();
+      return -1;       
+
+    case GE_INVALIDSMSLOCATION:
+
+      fprintf(stderr, _("Invalid location: %s %d\n"), memory_type_string, count);
+
+      break;
+
+    case GE_EMPTYSMSLOCATION:
+
+      fprintf(stderr, _("SMS location %s %d empty.\n"), memory_type_string, count);
+
+      break;
+
+    case GE_NOACCESS:
+
+      fprintf(stderr, _("No access to %s memory.\n"), memory_type_string);
+
+      break;
+
+    default:
+
+      fprintf(stderr, _("GetSMS %s %d failed!(%d)\n\n"), memory_type_string, count, error);
+    }
+  }
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+int getsmsstatus(int argc, char *argv[])
+{
+  GSM_SMSStatus SMSStatus;
+  GSM_SMSFolders folders;
+  GSM_Error error;
+  GSM_SMSMessage SMS;
+
+  int i,j;
+
+  /* Initialise the code for the GSM interface. */     
+  fbusinit(NULL);
+
+  error = GSM->GetSMSStatus(&SMSStatus);
+  if (error!=GE_NONE) return error;
+
+  fprintf(stdout, _("SMS Messages: UnRead %d, Number %d\n"),SMSStatus.UnRead, SMSStatus.Number);
+
+  error=GSM->GetSMSFolders(&folders);  
+  if (error!=GE_NONE) return error;
+
+  /* For not 7110 compatible phones we have to read all SMS and prepare sms table */
+  if( GetModelFeature (FN_SMS)!=F_SMS71 )
+  {
+    i=1;j=0;
+    while (true) {
+      if (j==SMSStatus.Number) break;
+      SMS.Location=i;
+      if (GSM->GetSMSMessage(&SMS)==GE_NONE) {
+        SMSStatus.foldertable[j].smsnum=i;
+
+        /* We set such folders ID like in 7110 compatible phones */
+        if (SMS.Status==GSS_NOTSENTREAD && SMS.folder==0) //GST_INBOX
+          SMSStatus.foldertable[j].folder=0;
+        else {
+          switch (SMS.folder) {
+            case 0://GST_INBOX
+              SMSStatus.foldertable[j].folder=GST_7110_INBOX;
+              break;
+            case 1://GST_OUTBOX
+              SMSStatus.foldertable[j].folder=GST_7110_OUTBOX;
+              break;
+          }
+        }
+        j++;
+      }
+      i++;
+    }
+  }
+
+  printf("0.Unread         : ");
+  for(j=0; j<SMSStatus.Number; j++)
+  {
+    if (SMSStatus.foldertable[j].folder == 0)
+      printf("%d ",SMSStatus.foldertable[j].smsnum);
+  }
+  printf("\n");
+
+  for (i=0;i<folders.number;i++) {
+    fprintf(stdout,_("%d.%-15s: "),i+1,folders.Folder[i].Name);
+    for(j=0; j<SMSStatus.Number; j++)
+    {
+      if ( SMSStatus.foldertable[j].folder / 8 == i+1)
+       printf("%d ",SMSStatus.foldertable[j].smsnum);
+    }
+    printf("\n");
+  }
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+/* Delete SMS messages. */
+int deletesms(int argc, char *argv[])
+{
+
+  GSM_SMSMessage message;
+  char memory_type_string[20];
+  int start_message, end_message, count;
+  GSM_Error error;
+
+  /* Handle command line args that set type, start and end locations. */
+  if (!GetMemoryTypeID(argv[0], &message.MemoryType))
+  {
+    fprintf(stderr, _("Unknown memory type %s!\n"), argv[0]);
+    return (-1);
+  }
+  GetMemoryTypeString(memory_type_string, &message.MemoryType);
+
+  start_message = atoi (argv[1]);
+  if (argc > 2) end_message = atoi (argv[2]);
+  else end_message = start_message;
+
+  /* Initialise the code for the GSM interface. */     
+
+  fbusinit(NULL);
+
+  /* Now delete the requested entries. */
+
+  for (count = start_message; count <= end_message; count ++) {
+
+    message.Location = count;
+
+    error = GSM->DeleteSMSMessage(&message);
+
+    if (error == GE_NONE)
+      fprintf(stdout, _("Deleted SMS %s %d\n"), memory_type_string, count);
+    else {
+      if (error == GE_NOTIMPLEMENTED) {
+       fprintf(stderr, _("Function not implemented in %s model!\n"), model);
+       GSM->Terminate();
+       return -1;      
+      }
+      fprintf(stdout, _("DeleteSMS %s %d failed!(%d)\n\n"), memory_type_string, count, error);
+    }
+  }
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+static volatile bool bshutdown = false;
+
+/* SIGINT signal handler. */
+
+static void interrupted(int sig)
+{
+
+  signal(sig, SIG_IGN);
+  bshutdown = true;
+
+}
+
+#ifdef SECURITY
+
+/* In this mode we get the code from the keyboard and send it to the mobile
+   phone. */
+
+int entersecuritycode(char *type)
+{
+  GSM_Error test;
+  GSM_SecurityCode SecurityCode;
+
+  if (!strcmp(type,"PIN"))      SecurityCode.Type=GSCT_Pin;
+  else if (!strcmp(type,"PUK")) SecurityCode.Type=GSCT_Puk;
+  else if (!strcmp(type,"PIN2"))SecurityCode.Type=GSCT_Pin2;
+  else if (!strcmp(type,"PUK2"))SecurityCode.Type=GSCT_Puk2;
+
+  // FIXME: Entering of SecurityCode does not work :-(
+  //  else if (!strcmp(type,"SecurityCode"))
+  //    SecurityCode.Type=GSCT_SecurityCode;
+
+  else {
+    fprintf(stdout, _("Wrong code in second parameter (allowed: PIN,PUK,PIN2,PUK2,SecurityCode)\n"));
+    return -1;
+  }
+
+#ifdef WIN32
+  printf("Enter your code: ");
+  gets(SecurityCode.Code);
+#else
+  strcpy(SecurityCode.Code,getpass(_("Enter your code: ")));
+#endif
+
+  fbusinit(NULL);
+
+  test = GSM->EnterSecurityCode(SecurityCode);
+  if (test==GE_NONE)
+    fprintf(stdout,_("Code OK !\n"));
+  else
+    fprintf(stderr,_("%s\n"),print_error(test));
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+int getsecuritycodestatus(void)
+{
+
+  int Status;
+
+  fbusinit(NULL);
+
+  if (GSM->GetSecurityCodeStatus(&Status) == GE_NONE) {
+
+    fprintf(stdout, _("Security code status: "));
+
+      switch(Status) {
+      case GSCT_SecurityCode:fprintf(stdout, _("waiting for Security Code.\n"));break;
+      case GSCT_Pin:         fprintf(stdout, _("waiting for PIN.\n"));          break;
+      case GSCT_Pin2:        fprintf(stdout, _("waiting for PIN2.\n"));         break;
+      case GSCT_Puk:         fprintf(stdout, _("waiting for PUK.\n"));          break;
+      case GSCT_Puk2:        fprintf(stdout, _("waiting for PUK2.\n"));         break;
+      case GSCT_None:        fprintf(stdout, _("nothing to enter.\n"));         break;
+      default:               fprintf(stdout, _("Unknown!\n"));
+      }
+  }
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+int getsecuritycode(char *type)
+{
+
+  GSM_SecurityCode SecurityCode;
+  GSM_Error error;
+
+  if (!strcmp(type,"PIN"))              SecurityCode.Type=GSCT_Pin;
+  else if (!strcmp(type,"PUK"))         SecurityCode.Type=GSCT_Puk;
+  else if (!strcmp(type,"PIN2"))        SecurityCode.Type=GSCT_Pin2;
+  else if (!strcmp(type,"PUK2"))        SecurityCode.Type=GSCT_Puk2;
+  else if (!strcmp(type,"SecurityCode"))SecurityCode.Type=GSCT_SecurityCode;
+  else {
+    fprintf(stdout, _("Wrong code in second parameter (allowed: PIN,PUK,PIN2,PUK2,SecurityCode)\n"));
+    return -1;
+  }
+    
+  fbusinit(NULL);
+
+  error=GSM->GetSecurityCode(&SecurityCode);
+  
+  switch (error) {
+    case GE_INVALIDSECURITYCODE:
+      fprintf(stdout, _("Error: getting "));
+      switch (SecurityCode.Type) {
+        case GSCT_SecurityCode:fprintf(stdout, _("security code"));break;
+        case GSCT_Pin :fprintf(stdout, _("PIN"));break;
+        case GSCT_Pin2:fprintf(stdout, _("PIN2"));break;
+        case GSCT_Puk :fprintf(stdout, _("PUK"));break;
+        case GSCT_Puk2:fprintf(stdout, _("PUK2"));break;
+       default:break;
+      }
+      fprintf(stdout, _(" not allowed\n"));
+      break;
+    case GE_NONE:
+      switch (SecurityCode.Type) {
+        case GSCT_SecurityCode:fprintf(stdout, _("Security code"));break;
+        case GSCT_Pin :fprintf(stdout, _("PIN"));break;
+        case GSCT_Pin2:fprintf(stdout, _("PIN2"));break;
+        case GSCT_Puk :fprintf(stdout, _("PUK"));break;
+        case GSCT_Puk2:fprintf(stdout, _("PUK2"));break;
+       default:break;
+      }
+      fprintf(stdout, _(" is %s\n"),SecurityCode.Code);
+      break;
+    default:
+      fprintf(stderr, _("%s\n"),print_error(error));
+      break;
+  }
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+#endif
+
+/* Voice dialing mode. */
+
+int dialvoice(char *Number)
+{
+  fbusinit(NULL);
+
+  if (GSM->DialVoice(Number)!=GE_NONE) fprintf(stdout,_("Error!\n"));
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+/* Cancel a call */
+int cancelcall(void)
+{
+  fbusinit(NULL);
+
+  if (GSM->CancelCall()!=GE_NONE) fprintf(stdout,_("Error!\n"));
+
+  GSM->Terminate();
+
+  return 0;
+}
+
+int savelogo(int argc, char *argv[])
+{
+  GSM_Bitmap bitmap;
+  GSM_NetworkInfo NetworkInfo;
+  GSM_MultiSMSMessage MultiSMS;
+
+  /* Operator logos will be saved with this number */  
+  char oplogonumber[]={'O','p','L','o','g','o',
+                      '0','0','0','0','0',   /* MMC+MNC */
+                      '\0'};
+  int i=0;
+  
+  bool UnicodeText=false;
+
+  /* The first argument is the type of the logo. */
+  if (!strcmp(argv[0], "op")) {
+    fprintf(stdout, _("Saving operator logo.\n"));
+  } else if (!strcmp(argv[0], "caller")) {
+    fprintf(stdout, _("Saving caller line identification logo.\n"));
+  } else if (!strcmp(argv[0], "startup")) {
+    fprintf(stderr, _("It isn't possible to save startup logo!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "7110startup")) {
+    fprintf(stderr, _("It isn't possible to save startup logo!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "6210startup")) {
+    fprintf(stderr, _("It isn't possible to save startup logo!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "7110op")) {
+    fprintf(stderr, _("It isn't possible to save big operator logos!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "picture")) {
+    fprintf(stderr, _("Saving picture image.\n"));
+  } else if (!strcmp(argv[0], "screensaver")) {
+    fprintf(stderr, _("Saving screen saver.\n"));
+  } else {
+    fprintf(stderr, _("You should specify what kind of logo to save!\n"));
+    return (-1);
+  }
+
+  /* The second argument is the bitmap file. */
+  if (GSM_ReadBitmapFileOnConsole(argv[1], &bitmap)!=GE_NONE) return -1;
+
+  /* Initialise the GSM interface. */
+  fbusinit(NULL);
+
+  /* We check optional parameters from 2'rd */
+  optind = 2;
+
+  if (!strcmp(argv[0], "op")) {
+    GSM_ResizeBitmap(&bitmap,GSM_CallerLogo);
+  
+    /* The third argument, if present, is the Network code of the operator.
+     * Network code is in this format: "xxx yy" */
+    if (argc > 2) {
+      strcpy(bitmap.netcode, argv[2]);
+#ifdef DEBUG
+      fprintf(stdout, _("Operator code: %s\n"), argv[2]);
+#endif
+      if (!strcmp(GSM_GetNetworkName(bitmap.netcode),"unknown")) {
+       fprintf(stderr,"Sorry, gnokii doesn't know \"%s\" network !\n",bitmap.netcode);
+       GSM->Terminate();
+       return -1;
+      }
+      optind++;
+    } else
+    {
+      if (GSM->GetNetworkInfo(&NetworkInfo) == GE_NONE) strncpy(bitmap.netcode,NetworkInfo.NetworkCode,7);
+    }
+    bitmap.type=GSM_OperatorLogo;
+
+    /* Put bitmap into SMS structure */
+    GSM_SaveBitmapToSMS(&MultiSMS,&bitmap,false,false);
+
+    oplogonumber[6]=bitmap.netcode[0];
+    oplogonumber[7]=bitmap.netcode[1];
+    oplogonumber[8]=bitmap.netcode[2];
+    oplogonumber[9]=bitmap.netcode[4];
+    oplogonumber[10]=bitmap.netcode[5];
+    for(i=0;i<MultiSMS.number;i++)
+      strcpy(MultiSMS.SMS[i].Destination,oplogonumber);
+  }
+  if (!strcmp(argv[0], "caller")) {
+    GSM_ResizeBitmap(&bitmap,GSM_CallerLogo);
+  
+    bitmap.type=GSM_CallerLogo;
+
+    /* Put bitmap into SMS structure */
+    GSM_SaveBitmapToSMS(&MultiSMS,&bitmap,false,false);
+
+    for(i=0;i<MultiSMS.number;i++)
+      strcpy(MultiSMS.SMS[i].Destination,"GroupLogo");
+  }
+  if (!strcmp(argv[0], "screensaver")) {
+    GSM_ResizeBitmap(&bitmap,GSM_PictureImage);
+
+    bitmap.text[0]=0;
+
+    for(i=0;i<argc;i++)
+      if (!strcmp(argv[i],"--unicode")) UnicodeText=true;
+
+    /* Put bitmap into SMS structure */
+    GSM_SaveBitmapToSMS(&MultiSMS,&bitmap,true,UnicodeText);
+
+    for(i=0;i<MultiSMS.number;i++)
+      strcpy(MultiSMS.SMS[i].Destination,"ScreenSaver");
+  }
+  if (!strcmp(argv[0], "picture")) {  
+    GSM_ResizeBitmap(&bitmap,GSM_PictureImage);
+
+    for(i=0;i<argc;i++)
+      if (!strcmp(argv[i],"--unicode")) UnicodeText=true;
+
+    bitmap.text[0]=0;
+    if (argc>2) {
+      optind++;
+      if (strlen(argv[2])>121) {
+        fprintf(stdout,_("Sorry: length of text (parameter \"%s\") can be 121 chars or shorter only !\n"),argv[2]);
+        return -1;
+      }
+      strcpy(bitmap.text,argv[2]);
+    }
+    
+    /* Put bitmap into SMS structure */
+    GSM_SaveBitmapToSMS(&MultiSMS,&bitmap,false,UnicodeText);
+
+    for(i=0;i<MultiSMS.number;i++)
+      strcpy(MultiSMS.SMS[i].Destination,"Picture");
+  }
+
+  GSM_SaveMultiPartSMSOnConsole(&MultiSMS, optind,argc,argv,false,true,false,false);
+  
+  return i;
+}
+
+/* The following function allows to send logos using SMS */
+int sendlogo(int argc, char *argv[])
+{
+  GSM_Bitmap bitmap;
+  GSM_NetworkInfo NetworkInfo;
+  GSM_MultiSMSMessage MultiSMS;
+
+  int i;
+
+  bool UnicodeText=false;
+  bool ScreenSaver=false;
+
+  /* The first argument is the type of the logo. */
+  if (!strcmp(argv[0], "op")) {
+    fprintf(stdout, _("Sending operator logo.\n"));
+  } else if (!strcmp(argv[0], "caller")) {
+    fprintf(stdout, _("Sending caller line identification logo.\n"));
+  } else if (!strcmp(argv[0], "picture")) {
+    fprintf(stdout, _("Sending picture image.\n"));
+  } else if (!strcmp(argv[0], "screensaver")) {
+    fprintf(stdout, _("Sending screen saver.\n"));
+  } else if (!strcmp(argv[0], "startup")) {
+    fprintf(stderr, _("It isn't possible to send startup logo!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "7110startup")) {
+    fprintf(stderr, _("It isn't possible to send startup logo!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "6210startup")) {
+    fprintf(stderr, _("It isn't possible to send startup logo!\n"));
+    return (-1);
+  } else if (!strcmp(argv[0], "7110op")) {
+    fprintf(stderr, _("It isn't possible to send big operator logos!\n"));
+    return (-1);
+  } else {
+    fprintf(stderr, _("You should specify what kind of logo to send!\n"));
+    return (-1);
+  }
+
+  /* The third argument is the bitmap file. */
+  if (GSM_ReadBitmapFileOnConsole(argv[2], &bitmap)!=GE_NONE) return -1;
+
+  /* Initialise the GSM interface. */
+  fbusinit(NULL);
+
+  optind = 3;
+
+  if (!strcmp(argv[0], "op")) {
+    GSM_ResizeBitmap(&bitmap,GSM_CallerLogo);
+  
+    /* The third argument, if present, is the Network code of the operator.
+     * Network code is in this format: "xxx yy" */
+    if (argc > 3) {
+      strcpy(bitmap.netcode, argv[3]);
+#ifdef DEBUG
+      fprintf(stdout, _("Operator code: %s\n"), argv[3]);
+#endif
+      if (!strcmp(GSM_GetNetworkName(bitmap.netcode),"unknown")) {
+       fprintf(stderr,"Sorry, gnokii doesn't know \"%s\" network !\n",bitmap.netcode);
+       GSM->Terminate();
+       return -1;
+      }
+      optind++;
+    } else
+    {
+      if (GSM->GetNetworkInfo(&NetworkInfo) == GE_NONE) strncpy(bitmap.netcode,NetworkInfo.NetworkCode,7);
+    }
+    bitmap.type=GSM_OperatorLogo;
+  }
+  if (!strcmp(argv[0], "caller")) {
+    GSM_ResizeBitmap(&bitmap,GSM_CallerLogo);
+  
+    bitmap.type=GSM_CallerLogo;
+  }
+  if (!strcmp(argv[0], "screensaver")) {
+    GSM_ResizeBitmap(&bitmap,GSM_PictureImage);
+
+    bitmap.text[0]=0;
+
+    for(i=0;i<argc;i++)
+      if (!strcmp(argv[i],"--unicode")) UnicodeText=true;
+    
+    ScreenSaver=true;
+  }
+  if (!strcmp(argv[0], "picture")) {  
+    GSM_ResizeBitmap(&bitmap,GSM_PictureImage);
+
+    for(i=0;i<argc;i++)
+      if (!strcmp(argv[i],"--unicode")) UnicodeText=true;
+
+    bitmap.text[0]=0;
+    if (argc>3) {
+      optind++;
+      if (strlen(argv[3])>121) {
+        fprintf(stdout,_("Sorry: length of text (parameter \"%s\") can be 121 chars or shorter only !\n"),argv[3]);
+        return -1;
+      }
+      strcpy(bitmap.text,argv[3]);
+    }
+  }
+
+  /* Put bitmap into SMS structure */
+  GSM_SaveBitmapToSMS(&MultiSMS,&bitmap,ScreenSaver,UnicodeText);
+
+  /* The second argument is the destination, ie the phone number of recipient. */
+  for(i=0;i<MultiSMS.number;i++)
+    strcpy(MultiSMS.SMS[i].Destination,argv[1]);
+
+  GSM_SendMultiPartSMSOnConsole(&MultiSMS, optind,argc,argv,true,false,false);
+
+  return i;
+}
+
+/* Getting logos. */
+
+int getlogo(int argc, char *argv[])
+{
+  GSM_Bitmap bitmap;
+  GSM_Error error;
+  int num;
+
+  bitmap.type=GSM_None;
+
+  if (!strcmp(argv[0],"7110op"))
+    bitmap.type=GSM_7110OperatorLogo;
+    
+  if (!strcmp(argv[0],"op"))
+    bitmap.type=GSM_OperatorLogo;
+    
+  if (!strcmp(argv[0],"caller")) {
+    /* There is caller group number missing in argument list. */
+    if (argc==3) {     
+      num=argv[2][0]-'0';
+      if ((num<1)||(num>9)) num=1;
+      bitmap.number=num;
+    } else
+    {
+      bitmap.number=1;
+    }
+    bitmap.number--;
+    bitmap.type=GSM_CallerLogo;
+  }
+
+  if (!strcmp(argv[0],"picture")) {
+    /* There is a number missing in argument list. */
+    if (argc==3) {     
+      if (strlen(argv[2])==2) {
+        num=(argv[2][0]-'0')*10+(argv[2][1]-'0');
+      } else {
+        num=argv[2][0]-'0';
+      }
+      if (num<1) num=1;
+      bitmap.number=num;
+    } else
+    {
+      bitmap.number=1;
+    }
+    bitmap.number--;
+    bitmap.type=GSM_PictureImage;
+  }    
+
+  if (!strcmp(argv[0],"startup"))
+    bitmap.type=GSM_StartupLogo;
+
+  if (!strcmp(argv[0],"7110startup"))
+    bitmap.type=GSM_7110StartupLogo;
+
+  if (!strcmp(argv[0],"6210startup"))
+    bitmap.type=GSM_6210StartupLogo;
+    
+  if (!strcmp(argv[0],"dealer"))
+    bitmap.type=GSM_DealerNoteText;  
+    
+  if (!strcmp(argv[0],"text"))
+    bitmap.type=GSM_WelcomeNoteText;  
+
+  if (bitmap.type!=GSM_None) {
+  
+    fbusinit(NULL);
+    
+    fprintf(stdout, _("Getting Logo\n"));
+        
+    error=GSM->GetBitmap(&bitmap);
+
+    GSM->Terminate();
+    
+    switch (error)
+    {
+      case GE_NONE:
+        if (bitmap.type==GSM_DealerNoteText) fprintf(stdout, _("Dealer welcome note "));
+        if (bitmap.type==GSM_WelcomeNoteText) fprintf(stdout, _("Welcome note "));     
+        if (bitmap.type==GSM_DealerNoteText || bitmap.type==GSM_WelcomeNoteText)
+       {
+          if (bitmap.text[0]!=0)
+         {
+           fprintf(stdout, _("currently set to \"%s\"\n"), bitmap.text);
+         } else {
+            fprintf(stdout, _("currently empty\n"));
+         }
+        } else
+        {
+          if (bitmap.width!=0)
+          {
+            if (bitmap.type==GSM_OperatorLogo || bitmap.type==GSM_7110OperatorLogo)
+           {
+              fprintf(stdout,"Operator logo for %s (%s) network got succesfully\n",bitmap.netcode,GSM_GetNetworkName(bitmap.netcode));
+           }
+            if (bitmap.type==GSM_StartupLogo || bitmap.type==GSM_7110StartupLogo || bitmap.type==GSM_6210StartupLogo)
+           {
+              fprintf(stdout,"Startup logo got successfully\n");
+           }
+            if (bitmap.type==GSM_CallerLogo)
+           {
+              fprintf(stdout,"Caller logo got successfully\n");
+           }
+            if (bitmap.type==GSM_PictureImage)
+           {
+              fprintf(stdout,"Picture Image got successfully");
+             if (strcmp(bitmap.text,""))
+                fprintf(stdout,_(", text \"%s\""),bitmap.text);                
+             if (strcmp(bitmap.Sender,""))
+                fprintf(stdout,_(", sender \"%s\""),bitmap.Sender);            
+              fprintf(stdout,"\n");
+           }
+            if (argc>1)
+           {
+             if (GSM_SaveBitmapFileOnConsole(argv[1], &bitmap)!=GE_NONE) return(-1);
+           }
+          } else
+          {
+            fprintf(stdout,"Your phone doesn't have logo uploaded !\n");
+           return -1;
+          }
+        }
+       break;
+      case GE_NOTIMPLEMENTED:
+        fprintf(stderr, _("Function not implemented !\n"));
+        return -1;
+      case GE_NOTSUPPORTED:
+        fprintf(stderr, _("This kind of logo is not supported !\n"));
+        return -1;
+      default:
+        fprintf(stderr, _("Error getting logo (wrong location ?) !\n"));
+        return -1;
+    }
+  } else
+  {
+    fprintf(stderr, _("What kind of logo do you want to get ?\n"));
+    return -1;
+  }
+
+  return 0;
+}
+
+/* Setting logos. */
+
+int setlogo(int argc, char *argv[])
+{
+
+  GSM_Bitmap bitmap,oldbit;
+  GSM_NetworkInfo NetworkInfo;
+  GSM_Error error;
+  char model[64];
+  int num;
+  
+  bool ok=true;
+  
+  int i;
+  
+  fbusinit(NULL);
+  
+  if (!strcmp(argv[0],"text") || !strcmp(argv[0],"dealer"))
+  {
+    if (!strcmp(argv[0],"text")) bitmap.type=GSM_WelcomeNoteText;
+                            else bitmap.type=GSM_DealerNoteText;
+    bitmap.text[0]=0x00;
+    if (argc>1) strncpy(bitmap.text,argv[1],255);
+  } else
+  {
+    if (!strcmp(argv[0],"op") || !strcmp(argv[0],"startup") || !strcmp(argv[0],"caller") ||
+        !strcmp(argv[0],"7110op") || !strcmp(argv[0],"6210startup") || !strcmp(argv[0],"7110startup") ||
+       !strcmp(argv[0],"picture"))
+    {
+      if (argc>1)
+      {
+        if (!strcmp(argv[0],"startup"))
+        {
+          bitmap.type=GSM_StartupLogo;
+          bitmap.width=84;
+          bitmap.height=48;
+         bitmap.size=GSM_GetBitmapSize(&bitmap);
+          num=argv[1][0]-'0';
+          if (num>=1 && num<=3) {
+           bitmap.number=num;
+          } else {
+            if (GSM_ReadBitmapFileOnConsole(argv[1], &bitmap)!=GE_NONE) {
+              GSM->Terminate();
+              return(-1);
+            }
+           bitmap.number=0;
+            GSM_ResizeBitmap(&bitmap,GSM_StartupLogo);
+          }
+        } else {
+          if (GSM_ReadBitmapFileOnConsole(argv[1], &bitmap)!=GE_NONE) {
+            GSM->Terminate();
+           return(-1);
+          }
+        }
+        if (!strcmp(argv[0],"op"))
+        {
+          if (bitmap.type!=GSM_OperatorLogo || argc<3)
+          {
+           if (GSM->GetNetworkInfo(&NetworkInfo) == GE_NONE) strncpy(bitmap.netcode,NetworkInfo.NetworkCode,7);
+         }
+          GSM_ResizeBitmap(&bitmap,GSM_OperatorLogo);
+         if (argc==3)
+         {
+           strncpy(bitmap.netcode,argv[2],7);
+           if (!strcmp(GSM_GetNetworkName(bitmap.netcode),"unknown"))
+           {
+             fprintf(stderr,"Sorry, gnokii doesn't know \"%s\" network !\n",bitmap.netcode);
+             return -1;
+           }
+         }
+        }
+        if (!strcmp(argv[0],"7110op"))
+        {
+          if (bitmap.type!=GSM_7110OperatorLogo || argc<3)
+          {
+           if (GSM->GetNetworkInfo(&NetworkInfo) == GE_NONE) strncpy(bitmap.netcode,NetworkInfo.NetworkCode,7);
+         }
+          GSM_ResizeBitmap(&bitmap,GSM_7110OperatorLogo);
+         if (argc==3)
+         {
+           strncpy(bitmap.netcode,argv[2],7);
+           if (!strcmp(GSM_GetNetworkName(bitmap.netcode),"unknown"))
+           {
+             fprintf(stderr,"Sorry, gnokii doesn't know \"%s\" network !\n",bitmap.netcode);
+             return -1;
+           }
+         }
+        }
+        if (!strcmp(argv[0],"picture"))
+        {
+          GSM_ResizeBitmap(&bitmap,GSM_PictureImage);
+         bitmap.number=1;
+         if (argc>2)
+         {
+            if (strlen(argv[2])==2) {
+              num=(argv[2][0]-'0')*10+(argv[2][1]-'0');
+            } else {
+              num=argv[2][0]-'0';
+            }
+            if (num<1) num=1;  
+           bitmap.number=num;
+          }
+         bitmap.number--;
+         bitmap.text[0]=0;
+         if (argc>3)
+           strncpy(bitmap.text,argv[3],121);
+         strcpy(bitmap.Sender,"\0");
+         if (argc>4)
+           strncpy(bitmap.Sender,argv[4],GSM_MAX_SENDER_LENGTH);
+        }
+        if (!strcmp(argv[0],"7110startup"))
+        {
+          GSM_ResizeBitmap(&bitmap,GSM_7110StartupLogo);
+        }
+        if (!strcmp(argv[0],"6210startup"))
+        {
+          GSM_ResizeBitmap(&bitmap,GSM_6210StartupLogo);
+        }
+        if (!strcmp(argv[0],"caller"))
+        {
+          GSM_ResizeBitmap(&bitmap,GSM_CallerLogo);
+          if (argc>2)
+         {
+           num=argv[2][0]-'0';
+           if ((num<0)||(num>9)) num=0;
+           bitmap.number=num;
+          } else
+         {
+           bitmap.number=0;
+         }
+          oldbit.type=GSM_CallerLogo;
+          oldbit.number=bitmap.number;
+          if (GSM->GetBitmap(&oldbit)==GE_NONE)
+         {
+            /* We have to get the old name and ringtone!! */
+           bitmap.ringtone=oldbit.ringtone;
+           strncpy(bitmap.text,oldbit.text,255);
+         }
+          if (argc>3) strncpy(bitmap.text,argv[3],255);          
+        }
+       fprintf(stdout, _("Setting Logo.\n"));
+      } else
+      {
+        /* FIX ME: is it possible to permanently remove op logo ? */
+        if (!strcmp(argv[0],"op"))
+        {
+         bitmap.type=GSM_OperatorLogo;
+         strncpy(bitmap.netcode,"000 00",7);
+         bitmap.width=72;
+         bitmap.height=14;
+         bitmap.size=GSM_GetBitmapSize(&bitmap);
+         GSM_ClearBitmap(&bitmap);
+       }
+        if (!strcmp(argv[0],"7110op"))
+        {
+         bitmap.type=GSM_7110OperatorLogo;
+         strncpy(bitmap.netcode,"000 00",7);
+         bitmap.width=78;
+         bitmap.height=21;
+          bitmap.size=GSM_GetBitmapSize(&bitmap);
+         GSM_ClearBitmap(&bitmap);
+       }
+       /* FIX ME: how to remove startup and group logos ? */
+       fprintf(stdout, _("Removing Logo.\n"));
+      }  
+    } else
+    {
+      fprintf(stderr, _("What kind of logo do you want to set ?\n"));
+      GSM->Terminate();
+      return -1;
+    }
+  }
+    
+  while (GSM->GetModel(model)  != GE_NONE)
+    sleep(1);
+  
+  /* For Nokia 6110/6130/6150 we use different method of uploading.
+     Phone will display menu, when received it */
+  if (!strcmp(model,"NSE-3") || !strcmp(model,"NSK-3") || !strcmp(model,"NSM-1"))
+  {
+    if (!strcmp(argv[0],"caller") && argc<3)
+      bitmap.number=255;
+    if (!strcmp(argv[0],"op") && argc<3)
+      bitmap.number=255;
+  }
+
+  error=GSM->SetBitmap(&bitmap);
+  
+  switch (error)
+  {
+    case GE_NONE: oldbit.type=bitmap.type;
+                  oldbit.number=bitmap.number;
+                  if (GSM->GetBitmap(&oldbit)==GE_NONE) {
+                   if (bitmap.type==GSM_WelcomeNoteText ||
+                       bitmap.type==GSM_DealerNoteText) {
+                     if (strcmp(bitmap.text,oldbit.text)) {
+                        fprintf(stderr, _("Error setting"));
+                       if (bitmap.type==GSM_DealerNoteText) fprintf(stderr, _(" dealer"));
+                       fprintf(stderr, _(" welcome note - "));
+
+                        /* I know, it looks horrible, but... */
+                       /* I set it to the short string - if it won't be set */
+                       /* it means, PIN is required. If it will be correct, previous */
+                       /* (user) text was too long */
+
+                       /* Without it, I could have such thing: */
+                       /* user set text to very short string (for example, "Marcin") */
+                       /* then enable phone without PIN and try to set it to the very long (too long for phone) */
+                       /* string (which start with "Marcin"). If we compare them as only length different, we could think, */
+                       /* that phone accepts strings 6 chars length only (length of "Marcin") */
+                       /* When we make it correct, we don't have this mistake */
+                       
+                       strcpy(oldbit.text,"!\0");
+                       GSM->SetBitmap(&oldbit);
+                       GSM->GetBitmap(&oldbit);
+                       if (oldbit.text[0]!='!') {
+                         fprintf(stderr, _("SIM card and PIN is required\n"));
+                       } else {
+                         GSM->SetBitmap(&bitmap);
+                         GSM->GetBitmap(&oldbit);
+                         fprintf(stderr, _("too long, truncated to \"%s\" (length %i)\n"),oldbit.text,strlen(oldbit.text));
+                       }
+                        ok=false;
+                     }
+                   } else {
+                     if (bitmap.type==GSM_StartupLogo) {
+                       for (i=0;i<oldbit.size;i++) {
+                         if (oldbit.bitmap[i]!=bitmap.bitmap[i]) {
+                           fprintf(stderr, _("Error setting startup logo - SIM card and PIN is required\n"));
+                           ok=false;
+                           break;
+                         }
+                       }
+                     }
+                   }
+                 }
+                 if (ok) fprintf(stdout, _("Done.\n"));
+                  break;
+    case GE_NOTIMPLEMENTED:fprintf(stderr, _("Function not implemented.\n"));
+                           break;
+    case GE_NOTSUPPORTED:fprintf(stderr, _("This kind of logo is not supported.\n"));
+                           break;
+    default:fprintf(stderr, _("Error (wrong location ?) !\n"));
+            break;
+  }
+  
+  GSM->Terminate();
+
+  return 0;
+}
+
+/* Calendar notes receiving. */
+
+int getcalendarnote(int argc, char *argv[])
+{
+  GSM_CalendarNote CalendarNote;
+  GSM_NotesInfo NotesInfo;
+  GSM_Error error;
+  int i;
+  int vCalVer=0;
+  bool vInfo=false;
+  int start, stop;
+  bool was_note=false;
+  char z_text[MAX_CALENDAR_TEXT_LENGTH+11];
+
+  /* Hopefully is 64 larger as FB38_MAX* / FB61_MAX* */
+  char model[64];
+
+  struct tm *now;
+  time_t nowh;
+  GSM_DateTime Date;
+
+  nowh=time(NULL);
+  now=localtime(&nowh);
+  
+  Date.Year = now->tm_year;
+
+  /* I have 100 (for 2000) Year now :-) */
+  if (Date.Year>99 && Date.Year<1900) {
+    Date.Year=Date.Year+1900;
+  }
+
+  start=atoi(argv[0]);  
+  stop=start;
+  
+  switch (argc) {
+    case 2:
+      if (!strcmp(argv[argc-1],"-v10")) {
+        vCalVer=10;
+      } else {
+        if (!strcmp(argv[argc-1],"-v30")) {
+          vCalVer=30;
+        } else {
+          stop=atoi(argv[1]);
+        }
+      }
+      break;
+    case 3:
+      stop=atoi(argv[1]);
+      if (!strcmp(argv[argc-1],"-v10")) {
+        vCalVer=10;
+      } else {
+        if (!strcmp(argv[argc-1],"-v30")) {
+          vCalVer=30;
+        } else {      
+          usage();
+          return -1;
+       }
+      }
+      break;
+  }
+
+  fbusinit(NULL);
+
+  while (GSM->GetModel(model)  != GE_NONE)
+    sleep(1);
+
+  if (!strcmp(argv[0],"-s") || !strcmp(argv[0],"--short")) 
+    vInfo=true;
+  else if (!isdigit(argv[0][0])) {
+    usage();
+    return -1;
+  }
+    
+  error=GSM->GetCalendarNotesInfo(&NotesInfo);
+  if ( error == GE_NONE ) {
+     if( NotesInfo.HowMany == 0 ) {
+        fprintf(stderr, _("Sorry! No Calendar Notes present on phone.\n"));
+        start=0; stop=(-1); /* This for skipping next 'for' loop ;-> */
+      }
+#ifdef DEBUG
+      fprintf(stdout, _(" CALENDAR NOTES INFO \n"));
+      fprintf(stdout, _("---------------------\n"));
+      fprintf(stdout, _("How Many Locations :%d\n"), NotesInfo.HowMany);
+
+      /* For 6210 (NPE-3) and 7110 (NSE-5), Locations have a different behaviour */
+      if ( GetModelFeature (FN_CALENDAR)==F_CAL71 ) {
+       fprintf(stdout, _("Locations are :\n"));
+       for(i=0;i<NotesInfo.HowMany;i++)
+           fprintf(stdout, _("%4d) %4d\n"), i+1, NotesInfo.Location[i]);
+      }
+#endif
+  } else {
+      /* For 6210 (NPE-3) and 7110 (NSE-5), Locations have a different behaviour */
+      if ( GetModelFeature (FN_CALENDAR)==F_CAL71 ) {
+        fprintf(stderr, _("Can't read Notes Infos from phone.\n"));
+       start=0; stop=(-1); /* This for skipping next 'for' loop ;-> */
+      }
+  }
+
+  if (GetModelFeature (FN_CALENDAR)!=F_CAL71) {
+    error=GE_NONE;
+    NotesInfo.HowMany=200;
+    for (i=0;i<200;i++) {
+      NotesInfo.Location[i]=i+1;
+    }
+  }
+  
+  if( vInfo && stop!=(-1) && error==GE_NONE )
+  {
+    /* Info datas (for 7110 and comp.) */
+    fprintf(stdout, _(" CALENDAR NOTES SUMMARY INFORMATION \n"));
+    fprintf(stdout, _(" ====================