This commit was generated by cvs2svn to compensate for changes in r4, which
[gnokii.git] / common / phones / nk2110.c
diff --git a/common/phones/nk2110.c b/common/phones/nk2110.c
new file mode 100644 (file)
index 0000000..bee7093
--- /dev/null
@@ -0,0 +1,1313 @@
+/* -*- linux-c -*-
+
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Copyright (C) 2000, 2001 Pavel Machek <pavel@ucw.cz>
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+  $Log$
+  Revision 1.1.1.1  2001/11/25 21:59:11  short
+  :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
+
+  Revision 1.16  2001/11/23 22:07:44  machek
+  Fix SMS receiving to work, again. Unfortunately, it is not possible to
+  reuse much of gsm-sms.c...
+
+  Revision 1.15  2001/11/15 12:10:57  pkot
+  Removed a warning from 2110 compilation
+
+  Revision 1.14  2001/11/09 13:49:12  pkot
+  Pavel! You haven't implemented slave_process() function!
+
+  Revision 1.13  2001/11/09 13:17:11  pkot
+  Update 2110 to a new libsms api
+
+  Revision 1.12  2001/10/21 22:23:56  machek
+  Use symbolic constants instead of numbers
+
+  At least detect when we get other message than we asked for
+
+  Provide option to passively wait for sms-es
+
+  Revision 1.11  2001/08/20 23:27:37  pkot
+  Add hardware shakehand to the link layer (Manfred Jonsson)
+
+  Revision 1.10  2001/07/17 22:46:27  pkot
+  Removed warning when compiling with --enable-debug (Pawel Kot)
+
+  Revision 1.9  2001/06/17 16:42:59  machek
+  Created another level of error message (ddprintf), fixed code not to
+  exit on error condition. Now it is actualy usable on my Philips Velo.
+
+  Revision 1.8  2001/06/16 10:00:41  machek
+  Implement timeouts on waiting for SMS.
+
+  Revision 1.7  2001/06/10 23:49:49  pkot
+  Small fixes to hide compilation warnings and allow gnokii.c to compile
+
+  Revision 1.6  2001/06/10 11:28:00  machek
+  Convert GetSMS/DeleteSMS to new structure.
+
+  Revision 1.5  2001/06/06 09:05:56  machek
+  Convert Grab/Release display to new structure.
+
+  Revision 1.4  2001/05/09 20:18:46  machek
+  Cleaned up code a bit. Made it use device_() interface. Reworked delay
+  system; now it is 4 times faster. 5 times faster if you hold * key on
+  phone (?!).
+
+  Revision 1.3  2001/05/07 14:13:03  machek
+  nokia-2110 module converted to suit new API better. --identify now works.
+
+  Revision 1.2  2001/04/27 16:00:01  machek
+  Better error messages.
+
+  Revision 1.1  2001/04/25 12:54:47  machek
+  Partly converted nokia 2110 to "new" form, and moved it to phone
+  directory.
+
+
+  Notice that this code was (partly) converted to "new" structure, but it 
+  does not have code for bus separated. I think that separating it would
+  be waste of effort...                                        --pavel
+
+*/
+
+#ifndef WIN32
+
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+
+#undef DEBUG
+#include "misc.h"
+#include "gsm-common.h"
+#include "mbus-2110.h"
+#include "phones/nokia.h"
+#include "device.h"
+#include "gsm-sms.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "misc.h"
+#include "gsm-common.h"
+#include "phones/generic.h"
+#include "phones/nk2110.h"
+
+#define MYID 0x78
+#define ddprintf(x...)
+#define eprintf(x...) fprintf(stderr, x)
+#undef DEBUG
+
+static GSM_Error P2110_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state);
+
+/* Global variables used by code in gsm-api.c to expose the
+   functions supported by this model of phone.  */
+bool N2110_LinkOK;
+static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
+static char *Revision = NULL,
+       *RevisionDate = NULL,
+       *Model = NULL,
+       VersionInfo[64];
+
+#define INFO \
+{ \
+       "2110|2140|6080",               /* Models */ \
+       100,                            /* Max RF Level */ \
+       0,                              /* Min RF Level */ \
+       GRF_Percentage,                 /* RF level units */ \
+       100,                            /* Max Battery Level */ \
+       0,                              /* Min Battery Level */ \
+       GBU_Percentage,                 /* Battery level units */ \
+       GDT_None,                       /* No date/time support */ \
+       GDT_None,                       /* No alarm support */ \
+       0,                              /* Max alarms = 0 */ \
+       0, 0,                           /* Startup logo size */ \
+       0, 0,                           /* Op logo size */ \
+       0, 0                            /* Caller logo size */ \
+}
+
+GSM_Information N2110_Information = INFO;
+
+GSM_Phone phone_nokia_2110 = {
+       NULL,
+       NULL,
+       INFO,
+       P2110_Functions
+};
+
+/* Local variables */
+static volatile bool RequestTerminate;
+static u8 TXPacketNumber = 0x01;
+#define MAX_MODEL_LENGTH 16
+static volatile unsigned char PacketData[10240];
+static volatile bool
+       ACKOK    = false,
+       PacketOK = false;
+
+static volatile int SMSpos = 0;
+static volatile unsigned char SMSData[10240];
+
+static long long LastChar = 0;
+
+static long long
+GetTime(void)
+{
+       struct timeval tv;
+
+       gettimeofday(&tv, NULL);
+       return (long long) tv.tv_sec * 1000000 + tv.tv_usec;
+}
+static void SigHandler(int status);
+#define POLLIT do { SigHandler(0); } while (0)
+
+static void
+yield(void)
+{
+       usleep(5000);
+}
+
+static void
+Wait(long long from, int msec)
+{
+       while (GetTime() < from + ((long long) msec)*1000) {
+               yield();
+               POLLIT;
+       }
+}
+
+static void
+Wait2(long long from, int msec)
+{
+       while (GetTime() < from + ((long long) msec)*1000) {
+               yield();
+       }
+}
+
+#define msleep(x) do { usleep(x*1000); } while (0)
+#define msleep_poll(x) do { Wait(GetTime(), x); } while (0)
+
+#define waitfor(condition, maxtime) \
+do { \
+       long long limit = GetTime() + maxtime*1000; \
+       if (!maxtime) limit = 0x7fffffffffffffffULL; \
+       while ((!(condition)) && (limit > GetTime())) { \
+               yield(); \
+               POLLIT; \
+        } \
+       if (!(limit > GetTime())) eprintf("???? TIMEOUT!"); \
+} while(0)
+
+/* Checksum calculation */
+static u8
+GetChecksum( u8 * packet, int len )
+{
+       u8 checksum = 0;
+       unsigned int i;
+
+       for( i = 0; i < len; i++ ) checksum ^= packet[i]; /* calculate checksum */
+       return checksum;
+}
+
+/* --------------- */
+
+static int xread(unsigned char *d, int len)
+{
+       int res;
+       while (len) {
+               res = device_read(d, len);
+               if (res == -1) {
+                       if (errno != EAGAIN) {
+                               dprintf("I/O error : %m?!\n");
+                               return -1;
+                       } else device_select(NULL);
+               } else {
+                       d += res;
+                       len -= res;
+                       dprintf("(%d)", len);
+               }
+       }
+       return 0;
+}
+
+static int xwrite(unsigned char *d, int len)
+{
+       int res;
+       while (len) {
+               res = device_write(d, len);
+               if (res == -1) {
+                       if (errno != EAGAIN) {
+                               dprintf("I/O error : %m?!\n");
+                               return -1;
+                       }
+               } else {
+                       d += res;
+                       len -= res;
+                       dprintf("<%d>", len);
+               }
+       }
+       return 0;
+}
+
+/* --------------------------- */
+
+static GSM_Error
+SendFrame( u8 *buffer, u8 command, u8 length )
+{
+       u8  pkt[10240], pkt2[10240];
+       int current = 0;
+
+       pkt[current++] = 0x00;               /* Construct the header.      */
+       pkt[current++] = MYID;
+       pkt[current++] = length;             /* Set data size              */
+       pkt[current++] = command;
+       memcpy( pkt + current, buffer, length ); /* Copy in data.          */
+       current += length;
+       pkt[current++] = TXPacketNumber;         /* Set packet number      */
+       pkt[current++] = GetChecksum( pkt, current); /* Calculate and set checksum */
+#ifdef DEBUG
+       {
+               int i;
+               u8  b;
+               fprintf( stderr, _("PC   : ") );
+               for( i = 0; i < current; i++ ) {
+                       b = pkt[i];
+                       fprintf( stderr, "[%02X %c]", b, b > 0x20 ? b : '.' );
+               }
+               fprintf( stderr, "\n" );
+       }
+#endif /* DEBUG */
+       /* Send it out... */
+       ddprintf("(");
+       Wait2(LastChar, 3);
+       /* I should put my messages at least 2msec apart... */
+       ddprintf(")");
+       dprintf("writing...");
+       LastChar = GetTime();
+       if (xwrite(pkt, current) == -1)
+               return (GE_INTERNALERROR);
+       if (xread(pkt2, current) == -1)
+               return (GE_INTERNALERROR);
+       dprintf("echook");
+       if (memcmp(pkt, pkt2, current)) {
+               eprintf("Bad echo?!");
+               msleep(1000);
+               return (GE_TIMEOUT);
+       }
+       return (GE_NONE);
+}
+
+static GSM_Error
+SendCommand( u8 *buffer, u8 command, u8 length )
+{
+       int time, retries = 10;
+       char pkt[10240];
+
+//     msleep(2);
+       while ((time = device_read(pkt, 10240)) != -1) {
+               int j;
+               char b;
+               dprintf("Spurious? (%d)", time);
+                                       dprintf( _("Phone: ") );
+                                       for( j = 0; j < time; j++ ) {
+                                               b = pkt[j];
+                                               dprintf( "[%02X %c]", b, b >= 0x20 ? b : '.' );
+                                       }
+               msleep(2);
+       }
+
+       ACKOK = false;
+       time = 30;
+       while (retries--) {
+               long long now;
+               SendFrame(buffer, command, length);
+               now = GetTime();
+               while ((GetTime() - now) < time*1000) {
+                       if (ACKOK)
+                               return GE_NONE;
+                       yield();
+                       POLLIT;
+               }
+               time = 50;              /* 5 seems to be enough */
+               dprintf("[resend]");
+       }
+       eprintf("Command not okay after 10 retries!\n");
+       return GE_BUSY;
+}
+
+/* Applications should call Terminate to close the serial port. */
+static void
+Terminate(void)
+{
+       /* Request termination of thread */
+       RequestTerminate = true;
+       device_close();
+}
+
+static GSM_Error
+SMS(GSM_SMSMessage *message, int command)
+{
+       u8 pkt[] = { 0x10, 0x02, 0, 0 };
+
+       SMSpos = 0;
+       memset((void *) &SMSData[0], 0, 255);
+       PacketOK = false;
+       pkt[1] = command;
+       pkt[2] = 1; /* == LM_SMS_MEM_TYPE_DEFAULT or LM_SMS_MEM_TYPE_SIM or LM_SMS_MEM_TYPE_ME */
+       pkt[3] = message->Number;
+
+       SendCommand(pkt, LM_SMS_COMMAND, sizeof(pkt));
+       msleep_poll(300);       /* We have to keep acknowledning phone's data */
+       waitfor(PacketOK, 1000);
+       if (!PacketOK) {
+               eprintf("SMS: No reply came within second!\n");
+       }
+       if (PacketData[3] != LM_SMS_EVENT) {
+               eprintf("Something is very wrong with SMS\n");
+               return GE_BUSY; /* FIXME */
+       }
+       if ((SMSData[2]) && (SMSData[2] != message->Number)) {
+               eprintf("Wanted message @%d, got message at @%d!\n", message->Number, SMSData[2]);
+               return GE_BUSY;
+       }
+       return (GE_NONE);
+}
+
+
+static GSM_Error
+DecodeIncomingSMS(GSM_SMSMessage *m)
+{
+       GSM_Error error;
+       int len, i;
+
+       error = GE_NONE;
+/*     Should be moved to gsm-sms.c */
+
+       ddprintf("Status: " );
+       switch (SMSData[3]) {
+       case 7: m->Type = SMS_Submit;  /* m->Status = GSS_NOTSENTREAD; */ ddprintf("not sent\n"); break;
+       case 5: m->Type = SMS_Submit;  /* m->Status = GSS_SENTREAD;    */ ddprintf("sent\n"); break;
+       case 3: m->Type = SMS_Deliver; /* m->Status = GSS_NOTSENTREAD; */ ddprintf("not read\n"); break;
+       case 1: m->Type = SMS_Deliver; /* m->Status = GSS_SENTREAD;    */ ddprintf("read\n"); break;
+       }
+
+       UnpackDateTime(SMSData+7, &m->Time);
+
+       m->Length = len = SMSData[14];
+       ddprintf("%d bytes: ", len );
+       for (i = 0; i<len; i++)
+               ddprintf("%c", SMSData[15+i]);
+       ddprintf("\n");
+
+       if (len>160)
+               eprintf("Magic not allowed\n");
+       memset(m->MessageText, 0, 161);
+       strncpy(m->MessageText, (void *) &SMSData[15], len);
+
+       ddprintf("Text is %s\n", m->MessageText);
+
+       /* 
+       Originator address is at 15+i,
+          followed by message center addres (?)
+       */
+       {
+               char *s = (char *) &SMSData[15+i];      /* We discard volatile. Make compiler quiet. */
+               strcpy(m->RemoteNumber.number, s);
+               s+=strlen(s)+1;
+               strcpy(m->MessageCenter.Number, s);
+               ddprintf("Sender = %s, MessageCenter = %s\n", m->Sender, m->MessageCenter.Name);
+       }
+
+       m->MessageCenter.No = 0;
+       strcpy(m->MessageCenter.Name, "(unknown)");
+       m->UDH_No = 0;
+
+       return error;
+}
+
+static GSM_Error
+GetSMSMessage(GSM_SMSMessage *m)
+{
+       if (m->Number > 10)
+               return GE_INVALIDSMSLOCATION;
+
+       if (SMS(m, LM_SMS_READ_STORED_DATA) != GE_NONE)
+               return GE_BUSY; /* FIXME */
+       ddprintf("Have message?\n");
+       
+       if (DecodeIncomingSMS(m) != GE_NONE)
+               return GE_BUSY;
+
+       if (SMSData[0] != LM_SMS_FORWARD_STORED_DATA) {
+               ddprintf("No sms there? (%x/%d)\n", SMSData[0], SMSpos);
+               return GE_EMPTYSMSLOCATION;
+       }
+       msleep_poll(1000);              /* If phone lost our ack, it might retransmit data */
+       return (GE_NONE);
+}
+
+#if 0
+static GSM_Error
+SendSMSMessage(GSM_SMSMessage *m)
+{
+       if (m->Number > 10)
+               return GE_INVALIDSMSLOCATION;
+
+       if (SMSData[0] != 0x0b) {
+               ddprintf("No sms there? (%x/%d)\n", SMSData[0], SMSpos);
+               return GE_EMPTYSMSLOCATION;
+       }
+       ddprintf("Status: " );
+       switch (SMSData[3]) {
+       case 7: m->Type = GST_MO; m->Status = GSS_NOTSENTREAD; ddprintf("not sent\n"); break;
+       case 5: m->Type = GST_MO; m->Status = GSS_SENTREAD; ddprintf("sent\n"); break;
+       case 3: m->Type = GST_MT; m->Status = GSS_NOTSENTREAD; ddprintf("not read\n"); break;
+       case 1: m->Type = GST_MT; m->Status = GSS_SENTREAD; ddprintf("read\n"); break;
+       }
+       return (GE_NONE);
+}
+#endif
+
+static GSM_Error       DeleteSMSMessage(GSM_SMSMessage *message)
+{
+       ddprintf("deleting...");
+       return SMS(message, 3);
+}
+
+/* GetRFLevel */
+
+static int
+GetValue(u8 index, u8 type)
+{
+       u8  pkt[] = {0x84, 2, 0};       /* Sending 4 at pkt[0] makes phone crash */
+       int val;
+       pkt[0] = index;
+       pkt[1] = type;
+
+       PacketOK = false;
+
+       ddprintf("\nRequesting value(%d)", index);
+       SendCommand(pkt, 0xe5, 3);
+
+       waitfor(PacketOK, 0);
+       if ((PacketData[3] != 0xe6) ||
+           (PacketData[4] != index) || 
+           (PacketData[5] != type))
+               dprintf("Something is very wrong with GetValue\n");
+       val = PacketData[7];
+       ddprintf( "Value = %d\n", val );
+       return (val);
+}
+
+static GSM_Error
+GetRFLevel(GSM_RFUnits *units, float *level)
+{
+       int val = GetValue(0x84, 2);
+       float res;
+       if (*units == GRF_Arbitrary) {
+               res = (100* (float) val) / 60.0;        /* This should be / 99.0 for some models other than nokia-2110 */
+               *level = 0;
+               if (res > 10)
+                       *level = 1;
+               if (res > 30)
+                       *level = 2;
+               if (res > 50)
+                       *level = 3;
+               if (res > 70)
+                       *level = 4;
+       } else {
+               *level = (100* (float) val) / 60.0;     /* This should be / 99.0 for some models other than nokia-2110 */
+               *units = GRF_Percentage;
+       }
+       return (GE_NONE);
+}
+
+static GSM_Error
+GetBatteryLevel(GSM_BatteryUnits *units, float *level)
+{
+       int val = GetValue(0x85, 2);
+       *level = 0;
+       if (val >= 5)
+               *level = 1;
+       if (val >= 10)
+               *level = 2;
+       if (val >= 90)
+               *level = 3;
+       if (*units == GBU_Arbitrary) {
+       } else {
+/*             *level = (100 * (float) val) / 90.0;*/  /* 5..first bar, 10..second bar, 90..third bar */
+               *level = *level * 33;
+               *units = GBU_Percentage;
+       }
+
+       return (GE_NONE);
+}
+
+static GSM_Error GetVersionInfo(void)
+{
+       char *s = VersionInfo;
+       ddprintf("Getting version info...\n");
+       if (GetValue(0x11, 0x03) == -1)
+               return GE_TIMEOUT;
+
+       strncpy( s, (void *) &PacketData[6], sizeof(VersionInfo) );
+
+       for( Revision     = s; *s != 0x0A; s++ ) if( !*s ) return (GE_NONE);
+       *s++ = 0;
+       for( RevisionDate = s; *s != 0x0A; s++ ) if( !*s ) return (GE_NONE);
+       *s++ = 0;
+       for( Model        = s; *s != 0x0A; s++ ) if( !*s ) return (GE_NONE);
+       *s++ = 0;
+       ddprintf("Revision %s, Date %s, Model %s\n", Revision, RevisionDate, Model );
+       return (GE_NONE);
+}
+
+/* Our "Not implemented" functions */
+static GSM_Error
+GetMemoryStatus(GSM_MemoryStatus *Status)
+{
+       switch(Status->MemoryType) {
+       case GMT_ME:
+               Status->Used = 0;
+               Status->Free = 150;
+               break;
+       case GMT_LD:
+               Status->Used = 5;
+               Status->Free = 0;
+               break;
+       case GMT_ON:
+               Status->Used = 1;
+               Status->Free = 0;
+               break;
+       case GMT_SM:
+               Status->Used = 0;
+               Status->Free = 150;
+               break;
+       default:
+               return (GE_NOTIMPLEMENTED);
+       }
+       return (GE_NONE);
+}
+
+static bool
+SendRLPFrame(RLP_F96Frame *frame, bool out_dtx)
+{
+       return (false);
+}
+
+static char *
+Display(u8 b, int shift, char *s, char *buf)
+{
+       b >>= shift;
+       b &= 0x03;
+       switch (b) {
+       case 0: break;
+       case 1: case 2: *buf++ = '!';
+       case 3: strcpy(buf, s); buf += strlen(s); *buf++ = ' ';
+       }
+       return buf;
+}
+
+static GSM_Error (*OutputFn)(char *text, char *ctrl);
+
+static int
+HandlePacket(void)
+{
+       eprintf("[%x]", PacketData[3]);
+       switch(PacketData[3]) {
+       case 0x12: {                    /* Text from display */
+               char buf[10240], *s = buf, *t;
+               t = (char *)&PacketData[8];
+#define COPY(x) strncpy(s, t, x); t+=x; s+=x; *s++ = '\n'
+               COPY(10); COPY(10); COPY(10); COPY(3); COPY(12); *s++ = 0;
+               if (OutputFn)
+                       (*OutputFn)(buf, NULL);
+               return 1;
+       }
+       case 0x2f: {                    /* Display lights */
+               char buf[10240], *s = buf;
+#undef COPY
+#define COPY(x, y, z) s = Display(PacketData[x], y, z, s)
+               /* Valid for 2110 */
+               COPY(4, 0, "d"); COPY(4, 2, "b"); COPY(4, 4, "a"); COPY(4, 6, "lights");
+               COPY(5, 0, "service"); COPY(5, 2, "scroll_up"); COPY(5, 4, "scroll_down"); COPY(5, 6, "ABC");
+               COPY(6, 0, "2.>"); COPY(6, 2, "1.>"); COPY(6, 4, "roam"); COPY(6, 6, "handset");
+               COPY(7, 0, "vmail"); COPY(7, 2, "envelope"); COPY(7, 4, "battbar"); COPY(7, 6, "3.>");
+               COPY(8, 0, "?1"); COPY(8, 2, "?2"); COPY(8, 4, "fieldbar"); COPY(8, 6, "ring");
+               *s++ = 0;
+               if (OutputFn)
+                       (*OutputFn)(NULL, buf);
+               return 1;
+#undef COPY
+       }
+       case LM_SMS_EVENT:              /* SMS Data */
+               /* copy bytes 5+ to smsbuf */
+               ddprintf("SMSdata:");
+               {
+                       int i;
+                       for (i=5; i<PacketData[2]+4; i++) {
+                               SMSData[SMSpos++] = PacketData[i];
+                               ddprintf("%c", PacketData[i]);
+                       }
+                       fflush(stdout);
+               }
+               return ((PacketData[4] & 0xf) != 0);
+               /* Make all but last fragment "secret" */
+
+       default: dprintf("Unknown response %dx\n", PacketData[3]); 
+                return 0;
+       }       
+}
+
+/* Handler called when characters received from serial port. 
+ * and process them. */
+static void
+SigHandler(int status)
+{
+       unsigned char        buffer[256], ack[5], b;
+       int                  i, res;
+       static unsigned int  Index = 0, Length = 5;
+       static unsigned char pkt[256];
+       int                  j;
+
+       res = device_read(buffer, 256);
+       if( res < 0 ) return;
+       for(i = 0; i < res ; i++) {
+               b = buffer[i];
+//      dprintf("(%x)", b, Index);
+               if (!Index && b != MYID && b != 0xf8 && b != 0x00) /* MYID is code of computer */ {
+                       /* something strange goes from phone. Just ignore it */
+                       ddprintf( "Get [%02X %c]\n", b, b >= 0x20 ? b : '.' );
+                       continue;
+               } else {
+                       pkt[Index++] = b;
+                       if(Index == 3) {
+                               Length = b + 6;
+                               if (b == 0x7f) Length = 5;
+                               if (b == 0x7e) Length = 8;
+                       }
+                       if(Index >= Length) {
+                               if((pkt[0] == MYID || pkt[0]==0xf8) && pkt[1] == 0x00) /* packet from phone */ {
+                                       ddprintf( _("Phone: ") );
+                                       for( j = 0; j < Length; j++ ) {
+                                               b = pkt[j];
+                                               ddprintf( "[%02X %c]", b, b >= 0x20 ? b : '.' );
+                                       }
+                                       ddprintf( "\n" );
+                                       /* ensure that we received valid packet */
+                                       if(pkt[Length - 1] != GetChecksum(pkt, Length-1)) {
+                                               eprintf( "***bad checksum***");
+                                       } else {
+                                               if((pkt[2] == 0x7F) || (pkt[2] == 0x7E)) /* acknowledge by phone */ {
+                                                       if (pkt[2] == 0x7F) {
+                                                               dprintf( "[ack]" );
+                                                               /* Set ACKOK flag */
+                                                               ACKOK    = true;
+                                                               /* Increase TX packet number */
+                                                       } else {
+                                                               dprintf( "[registration ack]" );
+                                                               N2110_LinkOK = true;
+                                                       }
+                                                       TXPacketNumber++;
+                                               } else {
+                                                       /* Copy packet data  */
+                                                       dprintf( "[data]" );
+                                                       memcpy((void *) PacketData,pkt,Length);
+                                                       /* send acknowledge packet to phone */
+                                                       msleep(10);
+                                                       ack[0] = 0x00;                     /* Construct the header.   */
+                                                       ack[1] = pkt[0];                   /* Send back id            */
+                                                       ack[2] = 0x7F;                     /* Set special size value  */
+                                                       ack[3] = pkt[Length - 2];          /* Send back packet number */
+                                                       ack[4] = GetChecksum( ack, 4); /* Set checksum            */
+                                                       ddprintf( _("PC   : ") );
+                                                       for( j = 0; j < 5; j++ ) {
+                                                               b = ack[j];
+                                                               ddprintf( "[%02X %c]", b, b >= 0x20 ? b : '.' );
+                                                       }
+                                                       ddprintf( "\n" );
+                                                       LastChar = GetTime();
+                                                       if( xwrite( ack, 5 ) == -1 )
+                                                               perror( _("Write error!\n") );
+                                                       if( xread( ack, 5 ) == -1 )
+                                                               perror( _("Read ack error!\n") );
+
+                                                       /* Set validity flag */
+                                                       if (!HandlePacket())
+                                                               PacketOK = true;
+                                               }
+                                       }
+                               } else
+                                       eprintf("Got my own echo? That should not be possible!\n");
+                               /* Look for new packet */
+                               Index  = 0;
+                               Length = 5;
+                       }
+               }
+       }
+}
+
+/* Called by initialisation code to open comm port in asynchronous mode. */
+bool OpenSerial(void)
+{
+       int result;
+
+       ddprintf(_("Setting MBUS communication with 2110...\n"));
+
+       result = device_open(PortDevice, true, false, false, GCT_Serial);
+       if (!result) { 
+               fprintf(stderr, "Failed to open %s ...\n", PortDevice);
+               return (false);
+       }
+
+       ddprintf("%s opened...\n", PortDevice);
+
+       device_changespeed(9600);
+       device_setdtrrts(1, 1);
+       return (true);
+}
+
+static GSM_Error
+SetKey(int c, int up)
+{
+       u8 reg[] = { 0x7a /* RPC_UI_KEY_PRESS or RPC_UI_KEY_RELEASE */, 0, 1, 0 /* key code */ };
+       reg[0] += up;
+       reg[3] = c;
+       dprintf("\n Pressing %d\n", c );
+       PacketOK = false;
+       SendCommand( reg, 0xe5, 4 );
+       waitfor(PacketOK, 0);
+       return GE_NONE;
+}
+
+#define XCTRL(a) (a&0x1f)
+#define POWER XCTRL('o')
+#define SEND XCTRL('t')
+#define END XCTRL('s')
+#define CLR XCTRL('h')
+#define MENU XCTRL('d')
+#define ALPHA XCTRL('a')
+#define PLUS XCTRL('b')
+#define MINUS XCTRL('e')
+#define PREV XCTRL('p')
+#define NEXT XCTRL('n')
+#define SOFTA XCTRL('x')
+#define SOFTB XCTRL('q')
+
+static char lastkey;
+
+static void PressKey(char c, int i)
+{
+       lastkey = c;
+#define X( a, b ) case a: SetKey(b, i); break;
+       switch (c) {
+       case '1' ... '9': SetKey(c-'0',i); break;
+       X('0', 10)
+       X('#', 11)
+       X('*', 12)
+       X(POWER, 13)
+       X(SEND, 14)
+       X(END, 15)
+       X(PLUS, 16)
+       X(MINUS, 17)
+       X(CLR, 18)
+       X(MENU, 21)
+       X(ALPHA, 22)
+       X(PREV, 23)
+       X(NEXT, 24)
+       X(SOFTA, 25)
+       X(SOFTB, 26)
+#if 0
+       X(STO, 19)      /* These are not present on 2110, so I can't test this. Enable once tested. */
+       X(RCL, 20)
+       X(MUTE, 28)
+#endif
+       default: fprintf(stderr, "Unknown key %d\n", c);
+       }
+#undef X
+}
+
+
+static void
+PressString(char *s, int upcase)
+{
+       static int lastchar;
+       static int lastupcase = 1;
+
+       if (lastchar == *s) {
+               fprintf(stderr, "***collision");
+               PressKey(ALPHA, 0);
+               PressKey(ALPHA, 0);
+               lastupcase = 1;
+       }
+
+       while (*s) {
+               lastchar = *s;
+               PressKey(*s, 0);
+               if (upcase != lastupcase) {
+                       fprintf(stderr, "***size change");
+                       msleep_poll(1500);
+                       PressKey(*s, 1);
+                       lastupcase = upcase;
+               }
+               s++;
+       }
+}
+
+/*
+ * This is able to press keys at 62letters/36seconds, tested on message
+ * "Tohle je testovaci zprava schvalne za jak dlouho ji to napise"
+ * Well, it is possible to write that message in 17seconds...
+ */
+static void
+HandleKey(char c)
+{
+       switch(c) {
+#define X(a, b) case a: PressString(b, 0); break;
+       X('-', "1");
+       X('?', "11");
+       X('!', "111");
+       X(',', "1111");
+       X('.', "11111");
+       X(':', "111111");
+       X('"', "1111111");
+       X('\'', "11111111");
+       X('&', "111111111");
+       X('$', "1111111111");
+/*     X('$', "11111111111"); libra, not in ascii */
+       X('(', "111111111111");
+       X(')', "1111111111111");
+       X('/', "11111111111111");
+       X('%', "111111111111111");
+       X('@', "1111111111111111");
+       X('_', "11111111111111111");
+       X('=', "111111111111111111");
+       X('a', "2");
+       X('b', "22");
+       X('c', "222");
+       X('d', "3");
+       X('e', "33");
+       X('f', "333");
+       X('g', "4");
+       X('h', "44");
+       X('i', "444");
+       X('j', "5");
+       X('k', "55");
+       X('l', "555");
+       X('m', "6");
+       X('n', "66");
+       X('o', "666");
+       X('p', "7");
+       X('q', "77");
+       X('r', "777");
+       X('s', "7777");
+       X('t', "8");
+       X('u', "88");
+       X('v', "888");
+       X('w', "9");
+       X('x', "99");
+       X('y', "999");
+       X('z', "9999");
+#undef X
+#define X(a, b) case a: PressString(b, 1); break;
+       X('A', "2");
+       X('B', "22");
+       X('C', "222");
+       X('D', "3");
+       X('E', "33");
+       X('F', "333");
+       X('G', "4");
+       X('H', "44");
+       X('I', "444");
+       X('J', "5");
+       X('K', "55");
+       X('L', "555");
+       X('M', "6");
+       X('N', "66");
+       X('O', "666");
+       X('P', "7");
+       X('Q', "77");
+       X('R', "777");
+       X('S', "7777");
+       X('T', "8");
+       X('U', "88");
+       X('V', "888");
+       X('W', "9");
+       X('X', "99");
+       X('Y', "999");
+       X('Z', "9999");
+#undef X
+       case ' ': PressKey('#', 0); break;
+       case '+': PressKey(ALPHA, 0); PressKey('*', 0); PressKey('*', 0);  PressKey(ALPHA, 0); break;
+       case '*': case '#':
+       case '0' ... '9': PressKey(ALPHA, 0); PressKey(c, 0); PressKey(ALPHA, 0); break;
+       default: PressKey(c, 0);
+       }
+}
+
+static GSM_Error
+HandleString(char *s)
+{
+       while (*s) {
+               HandleKey(*s);
+               s++;
+       }
+       fprintf(stderr,"***end of input");
+       PressKey(lastkey, 1);
+       return GE_NONE;
+}
+
+static void
+Register(void)
+{
+       u8 reg[] = { 1, 1, 0x0f, 1, 0x0f };
+       SendFrame( reg, 0xe9, 5 );
+}
+
+static GSM_Error
+EnableDisplayOutput(GSM_Statemachine *sm)
+{
+       /* LN_UC_SHARE, LN_UC_SHARE, LN_UC_RELEASE, LN_UC_RELEASE, LN_UC_KEEP */
+       u8  pkt[] = {3, 3, 0, 0, 1};
+
+       msleep_poll(500);
+       fprintf(stderr, "\nShould display output\n");
+       if (!OutputFn) {
+               pkt[0] = 0;
+               pkt[1] = 0;
+       }
+       PacketOK = false;
+       SendCommand(pkt, 0x19, 5);
+       fprintf(stderr, "\nGrabbing display");
+       waitfor(PacketOK, 0);
+       if ((PacketData[3] != 0xcd) ||
+           (PacketData[2] != 1) || 
+           (PacketData[4] != 1 /* LN_UC_REQUEST_OK */))
+               fprintf(stderr, "Something is very wrong with GrabDisplay\n");
+       fprintf(stderr, "Display grabbed okay (waiting)\n");
+       msleep_poll(500);
+       fprintf(stderr, "Okay\n");
+       return GE_NONE;
+}
+
+static GSM_Error SMS_Reserve(GSM_Statemachine *sm)
+{
+       u8 pkt[] = { 0x10, LM_SMS_RESERVE_PP, LN_SMS_NORMAL_RESERVE };
+       PacketOK = false;
+       SendCommand(pkt, LM_SMS_COMMAND, sizeof(pkt));
+       PacketOK = 0;
+       waitfor(PacketOK, 100);
+       if (!PacketOK)
+               eprintf("No reply trying to reserve SMS-es\n");
+       if (PacketData[3] != LM_SMS_EVENT)
+               eprintf("Bad reply trying to reserve SMS-es\n");
+       if (SMSData[0] != LM_SMS_PP_RESERVE_COMPLETE)
+               eprintf("Not okay trying to reserve SMS-es (%d)\n", SMSData[0]);
+       return GE_NONE;
+}
+
+static GSM_Error SMS_Slave(GSM_Statemachine *sm)
+{
+       SMS_Reserve(sm);
+       eprintf("Reserved okay\n");
+       while (1) {
+               PacketOK = 0;
+               SMSpos = 0;
+               memset((void *) &SMSData[0], 0, 255);
+               waitfor(PacketOK, 1000000);
+               if (PacketData[3] != LM_SMS_EVENT)
+                       eprintf("Wrong packet came!\n");
+               switch (SMSData[0]) {
+               case LM_SMS_RECEIVED_PP_DATA:
+                       eprintf("Data came!\n");
+                       break;
+               case LM_SMS_ALIVE_TEST:
+                       eprintf("Am I alive?\n");
+                       break;
+               case LM_SMS_NEW_MESSAGE_INDICATION:
+                       {
+                               GSM_SMSMessage m;
+                               eprintf("New message indicated @%d\n", SMSData[2]);
+                               msleep_poll(200);
+                               memset(&m, 0, sizeof(m));
+                               m.Number = SMSData[2];
+                               m.MemoryType = GMT_ME;
+                               if (GetSMSMessage(&m) != GE_NONE)
+                                       eprintf("Could not find promissed message?\n");
+#if 0
+                               else
+                                       slave_process(&m, SMSData[2]);
+#endif
+                       }
+                       break;
+               default:
+                       eprintf("Unexpected packet came: %x\n", SMSData[0]);
+               }
+       }
+}
+
+/* This is the main loop for the MB21 functions.  When N2110_Initialise
+          is called a thread is created to run this loop.  This loop is
+          exited when the application calls the N2110_Terminate function. */
+static void
+RegisterMe(void)
+{
+       fprintf(stderr, "Initializing... ");
+       /* Do initialisation stuff */
+       LastChar = GetTime();
+       if (OpenSerial() != true) {
+               N2110_LinkOK = false;
+               return;
+       }
+
+       msleep(100);
+       while(!N2110_LinkOK) {
+               fprintf(stderr, "registration... ");
+               Register();
+               msleep_poll(100);
+       }
+       fprintf(stderr, "okay\n");
+}
+
+/* Initialise variables and state machine. */
+static GSM_Error   
+Initialise(char *port_device, char *initlength, 
+          GSM_ConnectionType connection,
+          void (*rlp_callback)(RLP_F96Frame *frame))
+{
+       RequestTerminate = false;
+       N2110_LinkOK     = false;
+       setvbuf(stdout, NULL, _IONBF, 0);
+       setvbuf(stderr, NULL, _IONBF, 0);
+       memset(VersionInfo, 0, sizeof(VersionInfo));
+       strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
+       switch (connection) {
+       case GCT_Serial:
+               RegisterMe();
+               break;
+       default:
+               return GE_NOTSUPPORTED;
+               break;
+       }
+
+       return (GE_NONE);
+}
+
+/* Routine to get specifed phone book location.  Designed to be called by
+   application.  Will block until location is retrieved or a timeout/error
+   occurs. */
+
+static GSM_Error
+GetPhonebookLocation(GSM_PhonebookEntry *entry)
+{
+       u8  pkt[] = {0x1a, 0 /* 1 == phone */, 0};
+       int i;
+
+       pkt[1] = 3 + (entry->MemoryType != GMT_ME);
+       pkt[2] = entry->Location;
+       
+       PacketOK = false;
+       SendCommand(pkt, LN_LOC_COMMAND, 3);
+       waitfor(PacketOK, 0);
+       if ((PacketData[3] != 0xc9) ||
+           (PacketData[4] != 0x1a)) {
+               fprintf(stderr, "Something is very wrong with GetPhonebookLocation\n");
+               return GE_BUSY;
+       }
+       ddprintf("type= %x\n", PacketData[5]);
+       ddprintf("location= %x\n", PacketData[6]);
+       ddprintf("status= %x\n", PacketData[7]);
+       for (i=8; PacketData[i]; i++) {
+               ddprintf("%c", PacketData[i]);
+       }
+       strcpy(entry->Name, (void *)&PacketData[8]);
+       i++;
+       strcpy(entry->Number, (void *)&PacketData[i]);
+       for (; PacketData[i]; i++) {
+               ddprintf("%c", PacketData[i]);
+       }
+       ddprintf("\n");
+       entry->Empty = false;
+       entry->Group = 0;
+
+       return (GE_NONE);
+}
+
+/* Routine to write phonebook location in phone. Designed to be called by
+   application code. Will block until location is written or timeout
+   occurs. */
+
+static GSM_Error
+WritePhonebookLocation(GSM_PhonebookEntry *entry)
+{
+       u8  pkt[999] = {0x1b, 0 /* 1 == phone */, 0};
+
+       pkt[1] = 3 + (entry->MemoryType != GMT_ME);
+       pkt[2] = entry->Location;
+       strcpy(&pkt[3], entry->Name);
+       strcpy(&pkt[3+strlen(entry->Name)+1], entry->Number);
+       
+       PacketOK = false;
+       SendCommand(pkt, LN_LOC_COMMAND, 3+strlen(entry->Number)+strlen(entry->Name)+2);
+       waitfor(PacketOK, 0);
+       printf("okay?\n");
+       if ((PacketData[3] != 0xc9) ||
+           (PacketData[4] != 0x1b)) {
+               fprintf(stderr, "Something is very wrong with WritePhonebookLocation\n");
+               return GE_BUSY;
+       }
+       printf("type= %x\n", PacketData[5]);
+       printf("location= %x\n", PacketData[6]);
+       printf("status= %x\n", PacketData[7]);
+       return (GE_NONE);
+}
+
+static GSM_Error
+GetSMSStatus(GSM_SMSMemoryStatus *Status)
+{
+       Status->Unread = 0;
+       Status->Number = 5;
+       return GE_NONE;
+}
+
+
+GSM_Functions N2110_Functions = {
+       Initialise,
+       Terminate,
+       GetPhonebookLocation,
+       WritePhonebookLocation,
+       NULL,
+       NULL,
+       GetMemoryStatus,
+       GetSMSStatus,
+       NULL,
+       NULL,
+       GetSMSMessage,
+       DeleteSMSMessage,
+       NULL,
+       NULL,
+       GetRFLevel,
+       GetBatteryLevel,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       PNOK_GetManufacturer,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       SendRLPFrame,
+       NULL,
+       EnableDisplayOutput,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       SetKey,
+       HandleString,
+       NULL
+};
+
+#endif
+
+static GSM_Error link_Loop(struct timeval *tm)
+{
+       POLLIT;
+       return GE_NONE;
+}
+
+GSM_Error P2110_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
+{
+       GSM_Error err = GE_NONE;
+
+       printf("Asked for %d\n", op);
+       switch (op) {
+       case GOP_Init:
+               state->Link.Loop = link_Loop;
+               break;
+       case GOP_Identify:
+       case GOP_GetModel:
+       case GOP_GetRevision:
+               if (!Model) err = GetVersionInfo();
+               if (err) break;
+               if (data->Model) strncpy(data->Model, Model, 64);
+               if (data->Revision) strncpy(data->Revision, Revision, 64);
+               break;
+       case GOP_GetBatteryLevel:
+               err = GetBatteryLevel(data->BatteryUnits, data->BatteryLevel);
+               break;
+       case GOP_GetRFLevel:
+               err = GetRFLevel(data->RFUnits, data->RFLevel);
+               break;
+#if 0
+       case GOP_GetMemoryStatus:
+               err = N2110_GetMemoryStatus(data, state);
+               break;
+#endif
+       case GOP_DisplayOutput:
+               printf("DisplayOutput(%px)\n", data->OutputFn);
+               OutputFn = data->OutputFn;
+               printf("Enable\n");
+               err = EnableDisplayOutput(state);
+               break;
+       case GOP_GetSMS:
+#if 0
+               SMS_Slave(state);       /* FIXME!!! */
+#endif
+               msleep(100);
+               err = GetSMSMessage(data->SMSMessage);
+               break;
+       case GOP_DeleteSMS:
+               err = SMS(data->SMSMessage, 3);
+               break;
+       case GOP_ReadPhonebook:
+               err = GetPhonebookLocation(data->PhonebookEntry);
+               break;
+       case GOP_WritePhonebook:
+               err = WritePhonebookLocation(data->PhonebookEntry);
+               break;
+       case GOP_GetAlarm:
+               err = SMS_Slave(state);         /* Dirty hack, Fallthrough */
+               break;
+       default:
+               err = GE_NOTIMPLEMENTED;
+       }
+       return err;
+}