http://marcin-wiacek.fkn.pl/english/zips/mygnokii.tar.gz
[gnokii.git] / common / protocol / fbus.c
diff --git a/common/protocol/fbus.c b/common/protocol/fbus.c
new file mode 100644 (file)
index 0000000..46a6e8f
--- /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, 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, 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);
+}