--- /dev/null
+/*
+
+ $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.
+ Copyright (C) 2000 Chris Kemp
+
+ 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.13 2001/11/17 20:14:15 pkot
+ Nasty bug with counting message length. Workaround applied. Needs fixing.
+
+ Revision 1.12 2001/11/15 12:04:05 pkot
+ Faster initialization for 6100 series (don't check for dlr3 cable)
+
+ Revision 1.11 2001/09/09 21:45:49 machek
+ Cleanups from Ladislav Michl <ladis@psi.cz>:
+
+ *) do *not* internationalize debug messages
+
+ *) some whitespace fixes, do not use //
+
+ *) break is unneccessary after return
+
+ Revision 1.10 2001/08/20 23:27:37 pkot
+ Add hardware shakehand to the link layer (Manfred Jonsson)
+
+ Revision 1.9 2001/05/28 09:25:16 pkot
+ Fixed autodetecting of the cable type in 6110 and 7110 series. DLR-3 is
+ tried first now. Seems to work ok with either 6130 or 6210.
+
+ Revision 1.8 2001/05/07 16:24:03 pkot
+ DLR-3P temporary fix. How should I do it better?
+
+ Revision 1.7 2001/03/22 16:17:05 chris
+ Tidy-ups and fixed gnokii/Makefile and gnokii/ChangeLog which I somehow corrupted.
+
+ Revision 1.6 2001/03/21 23:36:05 chris
+ Added the statemachine
+ This will break gnokii --identify and --monitor except for 6210/7110
+
+ Revision 1.5 2001/03/19 23:44:56 pkot
+ DLR3 cable support
+
+ Revision 1.4 2001/03/13 01:24:02 pkot
+ Code cleanup - no warnings during compilation
+
+ Revision 1.3 2001/03/13 01:23:18 pkot
+ Windows updates (Manfred Jonsson)
+
+ Revision 1.2 2001/03/11 11:18:39 machek
+ Made fbus link_dispatch (and fixed minor memory leak)
+
+ Revision 1.1 2001/02/21 19:57:06 chris
+ More fiddling with the directory layout
+
+ Revision 1.1 2001/02/16 14:29:52 chris
+ Restructure of common/. Fixed a problem in fbus-phonet.c
+ Lots of dprintfs for Marcin
+ Any size xpm can now be loaded (eg for 7110 startup logos)
+ nk7110 code detects 7110/6210 and alters startup logo size to suit
+ Moved Marcin's extended phonebook code into gnokii.c
+
+ Revision 1.3 2001/02/06 21:15:34 chris
+ Preliminary irda support for 7110 etc. Not well tested!
+
+ Revision 1.2 2001/02/03 23:56:14 chris
+ Start of work on irda support (now we just need fbus-irda.c!)
+ Proper unicode support in 7110 code (from pkot)
+
+ Revision 1.1 2001/01/14 22:46:59 chris
+ Preliminary 7110 support (dlr9 only) and the beginnings of a new structure
+
+
+*/
+
+/* 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_c
+#include "links/fbus.h"
+
+/* FIXME - pass device_* the link stuff?? */
+/* FIXME - win32 stuff! */
+
+
+/* Some globals */
+
+static GSM_Link *glink;
+static GSM_Statemachine *statemachine;
+static FBUS_Link flink; /* FBUS specific stuff, internal to this file */
+
+
+/*--------------------------------------------*/
+
+bool FBUS_OpenSerial(bool dlr3)
+{
+ if (dlr3) dlr3 = 1;
+ /* Open device. */
+#ifdef WIN32
+ if (OpenConnection(glink->PortDevice, FBUS_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);
+
+ /* clearing the RTS bit and setting the DTR bit */
+ device_setdtrrts((1-dlr3), 0);
+
+ return (true);
+}
+
+
+/* RX_State machine for receive handling. Called once for each character
+ received from the phone. */
+
+void FBUS_RX_StateMachine(unsigned char rx_byte)
+{
+
+ struct timeval time_diff;
+ FBUS_IncomingFrame *i = &flink.i;
+ int frm_num, seq_num;
+ FBUS_IncomingMessage *m;
+
+#if 0
+ if (isprint(rx_byte))
+ dprintf("[%02x%c]", (unsigned char) rx_byte, rx_byte);
+ else
+ dprintf("[%02x ]", (unsigned char) rx_byte);
+#endif
+
+ /* XOR the byte with the current checksum */
+ i->checksum[i->BufferCount & 1] ^= rx_byte;
+
+ switch (i->state) {
+
+ /* Messages from the phone start with an 0x1e (cable) or 0x1c (IR).
+ 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:
+ gettimeofday(&i->time_now, NULL);
+ timersub(&i->time_now, &i->time_last, &time_diff);
+ if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
+ i->time_last = i->time_now; /* no gap seen, continue discarding */
+ break;
+ }
+
+ /* else fall through to... */
+
+ case FBUS_RX_Sync:
+ if (glink->ConnectionType == GCT_Infrared) {
+ if (rx_byte == FBUS_IR_FRAME_ID) {
+ /* Initialize checksums. */
+ i->checksum[0] = FBUS_IR_FRAME_ID;
+ i->checksum[1] = 0;
+ i->state = FBUS_RX_GetDestination;
+ } else {
+ /* Lost frame sync */
+ i->state = FBUS_RX_Discarding;
+ gettimeofday(&i->time_last, NULL);
+ }
+
+ } else { /* glink->ConnectionType == GCT_Serial */
+ if (rx_byte == FBUS_FRAME_ID) {
+ /* Initialize checksums. */
+ i->checksum[0] = FBUS_FRAME_ID;
+ i->checksum[1] = 0;
+ i->state = FBUS_RX_GetDestination;
+ } else {
+ /* Lost frame sync */
+ i->state = FBUS_RX_Discarding;
+ gettimeofday(&i->time_last, NULL);
+ }
+ }
+
+ break;
+
+ case FBUS_RX_GetDestination:
+
+ i->MessageDestination = rx_byte;
+ i->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 != 0x0c) {
+ i->state = FBUS_RX_Sync;
+ dprintf("The fbus stream is out of sync - expected 0x0c, got %2x\n", rx_byte);
+ }
+
+ break;
+
+ case FBUS_RX_GetSource:
+
+ i->MessageSource = rx_byte;
+ i->state = FBUS_RX_GetType;
+
+ /* Source should be 0x00 */
+
+ if (rx_byte != 0x00) {
+ i->state = FBUS_RX_Sync;
+ dprintf("The fbus stream is out of sync - expected 0x00, got %2x\n",rx_byte);
+ }
+
+ break;
+
+ case FBUS_RX_GetType:
+
+ i->MessageType = rx_byte;
+ i->state = FBUS_RX_GetLength1;
+
+ break;
+
+ case FBUS_RX_GetLength1:
+
+ i->FrameLength = rx_byte << 8;
+ i->state = FBUS_RX_GetLength2;
+
+ break;
+
+ case FBUS_RX_GetLength2:
+
+ i->FrameLength = i->FrameLength + rx_byte;
+ i->state = FBUS_RX_GetMessage;
+ i->BufferCount = 0;
+
+ break;
+
+ case FBUS_RX_GetMessage:
+
+ i->MessageBuffer[i->BufferCount] = rx_byte;
+ i->BufferCount++;
+
+ if (i->BufferCount > FBUS_MAX_FRAME_LENGTH) {
+ dprintf("FBUS: Message buffer overun - resetting\n");
+ i->state = FBUS_RX_Sync;
+ break;
+ }
+
+ /* If this is the last byte, it's the checksum. */
+
+ if (i->BufferCount == i->FrameLength + (i->FrameLength % 2) + 2) {
+ /* Is the checksum correct? */
+ if (i->checksum[0] == i->checksum[1]) {
+
+ /* Deal with exceptions to the rules - acks and rlp.. */
+
+ if (i->MessageType == 0x7f) {
+ dprintf("[Received Ack of type %02x, seq: %2x]\n",
+ i->MessageBuffer[0],(unsigned char) i->MessageBuffer[1]);
+
+ } else { /* Normal message type */
+
+ /* Add data to the relevant Message buffer */
+ /* having checked the sequence number */
+
+ m = &flink.messages[i->MessageType];
+
+ frm_num = i->MessageBuffer[i->FrameLength - 2];
+ seq_num = i->MessageBuffer[i->FrameLength - 1];
+
+
+ /* 0x40 in the sequence number indicates first frame of a message */
+
+ if ((seq_num & 0x40) == 0x40) {
+ /* Fiddle around and malloc some memory */
+ m->MessageLength = 0;
+ m->FramesToGo = frm_num;
+ if (m->Malloced != 0) {
+ free(m->MessageBuffer);
+ m->Malloced = 0;
+ m->MessageBuffer = NULL;
+ }
+ m->Malloced = frm_num * m->MessageLength;
+ m->MessageBuffer = (char *) malloc(m->Malloced);
+
+ } else if (m->FramesToGo != frm_num) {
+ dprintf("Missed a frame in a multiframe message.\n");
+ /* FIXME - we should make sure we don't ack the rest etc */
+ }
+
+ if (m->Malloced < m->MessageLength + i->FrameLength) {
+ m->Malloced = m->MessageLength + i->FrameLength;
+ m->MessageBuffer = (char *) realloc(m->MessageBuffer, m->Malloced);
+ }
+
+ memcpy(m->MessageBuffer + m->MessageLength, i->MessageBuffer,
+ i->FrameLength - 2);/* - (i->FrameLength % 2)); */
+
+ m->MessageLength += i->FrameLength - 2;/* - (i->FrameLength % 2); */
+
+ m->FramesToGo--;
+
+ /* Finally dispatch if ready */
+
+ if (m->FramesToGo == 0) {
+ SM_IncomingFunction(statemachine, i->MessageType, m->MessageBuffer, m->MessageLength);
+ free(m->MessageBuffer);
+ m->MessageBuffer = NULL;
+ m->Malloced = 0;
+ }
+
+ /* Send an ack (for all for now) */
+
+ FBUS_TX_SendAck(i->MessageType, seq_num & 0x0f);
+
+ }
+ } else {
+ dprintf("Bad checksum!\n");
+ }
+ i->state = FBUS_RX_Sync;
+ }
+ break;
+ }
+}
+
+
+/* This is the main loop function which must be called regularly */
+/* timeout can be used to make it 'busy' or not */
+
+GSM_Error FBUS_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++)
+ FBUS_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
+ (0x1e) and other values according the value specified when called.
+ Calculates checksum and then sends the lot down the pipe... */
+
+int FBUS_TX_SendFrame(u8 message_length, u8 message_type, u8 * buffer)
+{
+
+ u8 out_buffer[FBUS_MAX_TRANSMIT_LENGTH + 5];
+ int count, current = 0;
+ unsigned char checksum;
+
+ /* FIXME - we should check for the message length ... */
+
+ /* Now construct the message header. */
+
+ if (glink->ConnectionType == GCT_Infrared)
+ out_buffer[current++] = FBUS_IR_FRAME_ID; /* Start of the IR frame indicator */
+ else /* ConnectionType == GCT_Serial */
+ out_buffer[current++] = FBUS_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; /* Length */
+
+ out_buffer[current++] = message_length; /* Length */
+
+ /* 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
+ fprintf(stderr, _("PC: "));
+
+ for (count = 0; count < current; count++)
+ fprintf(stderr, "%02x:", out_buffer[count]);
+
+ fprintf(stderr, "\n");
+#endif /* DEBUG */
+
+ /* Send it out... */
+
+ if (device_write(out_buffer, current) != current)
+ return (false);
+
+ return (true);
+}
+
+
+/* Main function to send an fbus message */
+/* Splits up the message into frames if necessary */
+
+GSM_Error FBUS_SendMessage(u16 messagesize, u8 messagetype, void *message)
+{
+
+ u8 seqnum, frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
+ u8 nom, lml; /* number of messages, last message len */
+ int i;
+
+ seqnum = 0x40 + flink.RequestSequenceNumber;
+ flink.RequestSequenceNumber =
+ (flink.RequestSequenceNumber + 1) & 0x07;
+
+ if (messagesize > FBUS_MAX_CONTENT_LENGTH) {
+
+ nom = (messagesize + FBUS_MAX_CONTENT_LENGTH - 1)
+ / FBUS_MAX_CONTENT_LENGTH;
+ lml = messagesize - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
+
+ for (i = 0; i < nom - 1; i++) {
+
+ memcpy(frame_buffer, message + (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_TX_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2,
+ messagetype, frame_buffer);
+
+ seqnum = flink.RequestSequenceNumber;
+ flink.RequestSequenceNumber = (flink.RequestSequenceNumber + 1) & 0x07;
+ }
+
+ memcpy(frame_buffer, message + ((nom - 1) * FBUS_MAX_CONTENT_LENGTH), lml);
+ frame_buffer[lml] = 0x01;
+ frame_buffer[lml + 1] = seqnum;
+ FBUS_TX_SendFrame(lml + 2, messagetype, frame_buffer);
+
+ } else {
+
+ memcpy(frame_buffer, message, messagesize);
+ frame_buffer[messagesize] = 0x01;
+ frame_buffer[messagesize + 1] = seqnum;
+ FBUS_TX_SendFrame(messagesize + 2, messagetype,
+ frame_buffer);
+ }
+ return (GE_NONE);
+}
+
+
+int FBUS_TX_SendAck(u8 message_type, u8 message_seq)
+{
+
+ unsigned char request[2];
+
+ request[0] = message_type;
+ request[1] = message_seq;
+
+ dprintf("[Sending Ack of type %02x, seq: %x]\n",message_type, message_seq);
+
+ return FBUS_TX_SendFrame(2, 0x7f, request);
+}
+
+
+/* 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 FBUS_Initialise(GSM_Link *newlink, GSM_Statemachine *state, int type)
+{
+ unsigned char init_char = 0x55;
+ unsigned char count;
+
+ if (type > 2) return GE_DEVICEOPENFAILED;
+ /* 'Copy in' the global structures */
+ glink = newlink;
+ statemachine = state;
+
+ /* Fill in the link functions */
+ glink->Loop = &FBUS_Loop;
+ glink->SendMessage = &FBUS_SendMessage;
+
+ /* Check for a valid init length */
+ if (glink->InitLength == 0)
+ glink->InitLength = 250;
+
+ /* Start up the link */
+ flink.RequestSequenceNumber = 0;
+
+ if (glink->ConnectionType == GCT_Infrared) {
+ /* FIXME!! */
+ return GE_DEVICEOPENFAILED;
+ } else { /* ConnectionType == GCT_Serial */
+ /* FBUS_OpenSerial(0) - try dau-9p
+ * FBUS_OpenSerial(n != 0) - try dlr-3p */
+ if (!FBUS_OpenSerial(type))
+ 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(100);
+ device_write(&init_char, 1);
+ }
+
+ /* Init variables */
+ flink.i.state = FBUS_RX_Sync;
+ flink.i.BufferCount = 0;
+ for (count = 0; count < FBUS_MAX_MESSAGE_TYPES; count++) {
+ flink.messages[count].Malloced = 0;
+ flink.messages[count].FramesToGo = 0;
+ flink.messages[count].MessageLength = 0;
+ }
+
+ return GE_NONE;
+}