This commit was manufactured by cvs2svn to create branch 'lace'.
[gnokii.git] / common / links / fbus-3110.c
diff --git a/common/links/fbus-3110.c b/common/links/fbus-3110.c
new file mode 100644 (file)
index 0000000..5f98794
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+
+  $Id$
+
+  G N O K I I
+
+  A Linux/Unix toolset and driver for Nokia mobile phones.
+
+  Copyright (C) 2000 Hugh Blemings & Pavel Janík ml.
+
+  Released under the terms of the GNU GPL, see file COPYING for more details.
+
+  This file provides an API for accessing functions via fbus. 
+  See README for more details on supported mobile phones.
+
+  The various routines are called FBUS_(whatever).
+
+  $Log$
+  Revision 1.1.1.1  2001/11/25 21:59:10  short
+  :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
+
+  Revision 1.2  2001/11/09 14:25:04  pkot
+  DEBUG cleanups
+
+  Revision 1.1  2001/11/09 12:55:07  pkot
+  Forgot about fbus support for 3110. FIXME: is it really needed?
+
+
+*/
+
+/* System header files */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/* Various header file */
+
+#include "config.h"
+#include "misc.h"
+#include "gsm-common.h"
+#include "gsm-ringtones.h"
+#include "gsm-networks.h"
+#include "gsm-statemachine.h"
+#include "links/utils.h"
+
+#ifndef WIN32
+  #include "device.h"
+#else
+  #include "win32/winserial.h"
+  #define device_write(a, b) WriteCommBlock(a, b)
+  #define device_read(a, b) ReadCommBlock(a, b)
+  #define sleep(x) Sleep((x) * 1000)
+  #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
+#endif
+
+#define __links_fbus_3110_c
+#include "links/fbus-3110.h"
+
+/* FIXME - pass device_* the link stuff?? */
+/* FIXME - win32 stuff! */
+
+
+/* Some globals */
+
+static GSM_Link *glink;
+static GSM_Statemachine *statemachine;
+static FB3110_Link flink;              /* FBUS specific stuff, internal to this file */
+
+
+/*--------------------------------------------*/
+
+bool FB3110_OpenSerial(void)
+{
+       /* Open device. */
+#ifdef WIN32
+       if (OpenConnection(glink->PortDevice, FB3110_RX_StateMachine, NULL)) {
+#else
+       if (!device_open(glink->PortDevice, false, false, false, GCT_Serial)) {
+#endif
+               perror(_("Couldn't open FBUS device"));
+               return false;
+       }
+       device_changespeed(115200);
+
+       return (true);
+}
+
+
+/* RX_State machine for receive handling.  Called once for each character
+   received from the phone. */
+
+void FB3110_RX_StateMachine(unsigned char rx_byte)
+{
+       FB3110_IncomingFrame *i = &flink.i;
+       int count;
+
+       switch (i->State) {
+
+               /* Phone is currently off.  Wait for 0x55 before
+                  restarting */
+        case FB3110_RX_Discarding:
+                if (rx_byte != 0x55)
+                       break;
+
+                /* Seen 0x55, restart at 0x04 */
+                i->State = FB3110_RX_Sync;
+
+               dprintf("restarting.\n");
+
+                /* FALLTHROUGH */
+
+               /* Messages from the phone start with an 0x04 during
+                  "normal" operation, 0x03 when in data/fax mode.  We
+                  use this to "synchronise" with the incoming data
+                  stream. */
+       case FB3110_RX_Sync:
+               if (rx_byte == 0x04 || rx_byte == 0x03) {
+                       i->FrameType = rx_byte;
+                       i->Checksum = rx_byte;
+                       i->State = FB3110_RX_GetLength;
+               }
+               break;
+
+               /* Next byte is the length of the message including
+                  the message type byte but not including the checksum. */
+        case FB3110_RX_GetLength:
+               i->FrameLength = rx_byte;
+               i->BufferCount = 0;
+               i->Checksum ^= rx_byte;
+                i->State = FB3110_RX_GetMessage;
+                break;
+
+               /* Get each byte of the message.  We deliberately
+                  get one too many bytes so we get the checksum
+                  here as well. */
+       case FB3110_RX_GetMessage:
+               i->Buffer[i->BufferCount] = rx_byte;
+               i->BufferCount++;
+
+               if (i->BufferCount > FB3110_MAX_FRAME_LENGTH) {
+                       dprintf("FBUS: Message buffer overun - resetting\n");
+                       i->State = FB3110_RX_Sync;
+                       break;
+               }
+
+               /* If this is the last byte, it's the checksum. */
+               if (i->BufferCount > i->FrameLength) {
+
+                       /* Is the checksum correct? */
+                       if (rx_byte == i->Checksum) {
+
+                               if (i->FrameType == 0x03) {
+                                       /* FIXME: modify Buffer[0] to code FAX frame types */
+                               }
+
+                               dprintf("--> %02x:%02x:", i->FrameType, i->FrameLength);
+                               for (count = 0; count < i->BufferCount; count++)
+                                       dprintf("%02hhx:", i->Buffer[count]);
+                               dprintf("\n");
+                               /* Transfer message to state machine */
+                               SM_IncomingFunction(statemachine, i->Buffer[0], i->Buffer, i->FrameLength);
+
+                               /* Send an ack */
+                               FB3110_TX_SendAck(i->Buffer, i->FrameLength);
+
+                       } else {
+                               /* Checksum didn't match so ignore. */
+                               dprintf("Bad checksum!\n");
+                       }
+                       i->State = FB3110_RX_Sync;
+               }
+               i->Checksum ^= rx_byte;
+               break;
+       }
+}
+
+
+/* This is the main loop function which must be called regularly */
+/* timeout can be used to make it 'busy' or not */
+
+GSM_Error FB3110_Loop(struct timeval *timeout)
+{
+#ifndef WIN32
+       unsigned char buffer[255];
+       int count, res;
+
+       res = device_select(timeout);
+       if (res > 0) {
+               res = device_read(buffer, 255);
+               for (count = 0; count < res; count++)
+                       FB3110_RX_StateMachine(buffer[count]);
+       } else
+               return GE_TIMEOUT;
+
+       /* This traps errors from device_read */
+       if (res > 0)
+               return GE_NONE;
+       else
+               return GE_INTERNALERROR;
+#else
+       return GE_NONE;
+#endif
+}
+
+
+
+/* Prepares the message header and sends it, prepends the
+   message start byte (0x01) and other values according
+   the value specified when called.  Calculates checksum
+   and then sends the lot down the pipe... */
+
+GSM_Error FB3110_TX_SendFrame(u8 message_length, u8 message_type, u8 sequence_byte, u8 * buffer)
+{
+
+       u8 out_buffer[FB3110_MAX_TRANSMIT_LENGTH];
+       int count, current = 0;
+       unsigned char checksum;
+
+        /* Check message isn't too long, once the necessary
+           header and trailer bytes are included. */
+       if ((message_length + 5) > FB3110_MAX_TRANSMIT_LENGTH) {
+               fprintf(stderr, _("FB3110_TX_SendFrame - message too long!\n"));
+               return (GE_INTERNALERROR);
+       }
+
+       /* Now construct the message header. */
+       out_buffer[current++] = FB3110_FRAME_ID;    /* Start of frame */
+       out_buffer[current++] = message_length + 2; /* Length */
+       out_buffer[current++] = message_type;       /* Type */
+       out_buffer[current++] = sequence_byte;      /* Sequence number */
+
+       /* Copy in data if any. */
+       if (message_length != 0) {
+               memcpy(out_buffer + current, buffer, message_length);
+               current += message_length;
+       }
+
+        /* Now calculate checksum over entire message 
+           and append to message. */
+       checksum = 0;
+       for (count = 0; count < current; count++)
+               checksum ^= out_buffer[count];
+       out_buffer[current++] = checksum;
+
+       dprintf("<-- ");
+       for (count = 0; count < current; count++)
+               dprintf("%02hhx:", out_buffer[count]);
+       dprintf("\n");
+
+       /* Send it out... */
+       if (device_write(out_buffer, current) != current)
+               return (GE_INTERNALERROR);
+
+       return (GE_NONE);
+}
+
+
+/* Main function to send an fbus message */
+
+GSM_Error FB3110_SendMessage(u16 messagesize, u8 messagetype, void *message)
+{
+       u8 seqnum;
+
+       FB3110_UpdateSequenceNumber();
+       seqnum = flink.RequestSequenceNumber;
+
+       return FB3110_TX_SendFrame(messagesize, messagetype, seqnum, message);
+}
+
+
+/* Sends the "standard" acknowledge message back to the phone in
+   response to a message it sent automatically or in response to
+   a command sent to it.  The ack. algorithm isn't 100% understood
+   at this time. */
+
+void FB3110_TX_SendAck(u8 *message, int length)
+{
+       u8 t = message[0];
+
+       switch (t) {
+       case 0x0a:
+               /* We send 0x0a messages to make a call so don't ack. */
+       case 0x0c:
+               /* We send 0x0c message to answer to incoming call 
+                  so don't ack */
+       case 0x0f:
+               /* We send 0x0f message to hang up so don't ack */
+       case 0x15:
+               /* 0x15 messages are sent by the phone in response to the
+                  init sequence sent so we don't acknowledge them! */
+       case 0x20:
+               /* We send 0x20 message to phone to send DTFM, so don't ack */
+       case 0x23:
+               /* We send 0x23 messages to phone as a header for outgoing SMS
+                  messages.  So we don't acknowledge it. */
+       case 0x24:
+               /* We send 0x24 messages to phone as a header for storing SMS
+                  messages in memory. So we don't acknowledge it. :) */
+       case 0x25:
+               /* We send 0x25 messages to phone to request an SMS message
+                  be dumped.  Thus we don't acknowledge it. */
+       case 0x26:
+               /* We send 0x26 messages to phone to delete an SMS message
+                  so it's not acknowledged. */
+       case 0x3f:
+               /* We send an 0x3f message to the phone to request a different
+                  type of status dump - this one seemingly concerned with 
+                  SMS message center details.  Phone responds with an ack to
+                  our 0x3f request then sends an 0x41 message that has the
+                  actual data in it. */
+       case 0x4a:
+               /* 0x4a message is a response to our 0x4a request, assumed to
+                  be a keepalive message of sorts.  No response required. */
+       case 0x4c:
+               /* We send 0x4c to request IMEI, Revision and Model info. */
+               break;
+       case 0x27:
+               /* 0x27 messages are a little different in that both ends of
+                  the link send them.  So, first we have to check if this
+                  is an acknowledgement or a message to be acknowledged */
+               if (length == 0x02) break;
+       default:
+               /* Standard acknowledge seems to be to return an empty message
+                  with the sequence number set to equal the sequence number
+                  sent minus 0x08. */
+               if (FB3110_TX_SendFrame(0, t, (message[1] & 0x1f) - 0x08, NULL) != GE_NONE)
+                       dprintf("Failed to acknowledge message type %02x.\n", t);
+               else
+                       dprintf("Acknowledged message type %02x.\n", t);
+       }
+}
+
+
+/* Initialise variables and start the link */
+/* newlink is actually part of state - but the link code should not anything about state */
+/* state is only passed around to allow for muliple state machines (one day...) */
+
+GSM_Error FB3110_Initialise(GSM_Link *newlink, GSM_Statemachine *state)
+{
+       unsigned char init_char = 0x55;
+       unsigned char count;
+       static int try = 0;
+
+       try++;
+       if (try > 2) return GE_DEVICEOPENFAILED;
+       /* 'Copy in' the global structures */
+       glink = newlink;
+       statemachine = state;
+
+       /* Fill in the link functions */
+       glink->Loop = &FB3110_Loop;
+       glink->SendMessage = &FB3110_SendMessage;
+
+       /* Check for a valid init length */
+       if (glink->InitLength == 0)
+               glink->InitLength = 100;
+
+       /* Start up the link */
+
+       flink.RequestSequenceNumber = 0x10;
+
+       if (!FB3110_OpenSerial()) return GE_DEVICEOPENFAILED;
+
+       /* Send init string to phone, this is a bunch of 0x55 characters. 
+          Timing is empirical. I believe that we need/can do this for any 
+          phone to get the UART synced */
+       for (count = 0; count < glink->InitLength; count++) {
+               usleep(1000);
+               device_write(&init_char, 1);
+       }
+
+       /* Init variables */
+       flink.i.State = FB3110_RX_Sync;
+
+       return GE_NONE;
+}
+
+
+/* Any command we originate must have a unique SequenceNumber.
+   Observation to date suggests that these values startx at 0x10
+   and cycle up to 0x17 before repeating again.  Perhaps more
+   accurately, the numbers cycle 0,1,2,3..7 with bit 4 of the byte
+   premanently set. */
+
+void FB3110_UpdateSequenceNumber(void)
+{
+    flink.RequestSequenceNumber++;
+
+    if (flink.RequestSequenceNumber > 0x17 || flink.RequestSequenceNumber < 0x10) {
+        flink.RequestSequenceNumber = 0x10;
+    }
+}