summaryrefslogtreecommitdiff
path: root/libgammu/protocol/nokia/fbus2.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgammu/protocol/nokia/fbus2.c')
-rw-r--r--libgammu/protocol/nokia/fbus2.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/libgammu/protocol/nokia/fbus2.c b/libgammu/protocol/nokia/fbus2.c
new file mode 100644
index 0000000..1add22d
--- /dev/null
+++ b/libgammu/protocol/nokia/fbus2.c
@@ -0,0 +1,515 @@
+/* (c) 2002-2003 by Marcin Wiacek */
+/* based on some work from MyGnokii (www.mwiacek.com) */
+/* Based on some work from Gnokii (www.gnokii.org)
+ * (C) 1999-2000 Hugh Blemings & Pavel Janik ml. (C) 2001-2004 Pawel Kot
+ * GNU GPL version 2 or later
+ */
+/* Due to a problem in the source code management, the names of some of
+ * the authors have unfortunately been lost. We do not mean to belittle
+ * their efforts and hope they will contact us to see their names
+ * properly added to the Copyright notice above.
+ * Having published their contributions under the terms of the GNU
+ * General Public License (GPL) [version 2], the Copyright of these
+ * authors will remain respected by adhering to the license they chose
+ * to publish their code under.
+ */
+
+#include "../../gsmstate.h"
+
+#if defined(GSM_ENABLE_FBUS2) || defined(GSM_ENABLE_FBUS2IRDA) || defined(GSM_ENABLE_FBUS2DLR3) || defined(GSM_ENABLE_FBUS2BLUE) || defined(GSM_ENABLE_BLUEFBUS2) || defined(GSM_ENABLE_DKU5FBUS2) || defined(GSM_ENABLE_FBUS2PL2303)
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../../gsmcomon.h"
+#include "fbus2.h"
+
+static GSM_Error FBUS2_Initialise(GSM_StateMachine *s);
+
+static GSM_Error FBUS2_WriteFrame(GSM_StateMachine *s,
+ unsigned const char *MsgBuffer,
+ int MsgLength,
+ unsigned char MsgType)
+{
+ unsigned char buffer[FBUS2_MAX_TRANSMIT_LENGTH + 10]={0};
+ unsigned char checksum=0;
+ int i=0, length=0, sent=0;
+
+ buffer[0] = FBUS2_FRAME_ID;
+
+ if (s->ConnectionType==GCT_FBUS2IRDA) {
+ buffer[0] = FBUS2_IRDA_FRAME_ID;
+ }
+ buffer[1] = FBUS2_DEVICE_PHONE; /* destination */
+ buffer[2] = FBUS2_DEVICE_PC; /* source */
+ buffer[3] = MsgType;
+ buffer[4] = MsgLength / 256;
+ buffer[5] = MsgLength % 256;
+ memcpy(buffer + 6, MsgBuffer, MsgLength);
+ length = MsgLength + 6;
+
+ /* Odd messages require additional 0x00 byte */
+ if (MsgLength % 2) {
+ buffer[length++] = 0x00;
+ }
+ checksum = 0;
+
+ for (i = 0; i < length; i+=2) {
+ checksum ^= buffer[i];
+ }
+ buffer[length++] = checksum;
+ checksum = 0;
+
+ for (i = 1; i < length; i+=2) {
+ checksum ^= buffer[i];
+ }
+ buffer[length++] = checksum;
+
+ /* Sending to phone */
+ sent = s->Device.Functions->WriteDevice(s, buffer, length);
+
+ if (sent != length) {
+ return ERR_DEVICEWRITEERROR;
+ }
+ return ERR_NONE;
+}
+
+static GSM_Error FBUS2_WriteMessage (GSM_StateMachine *s,
+ unsigned const char *MsgBuffer,
+ size_t MsgLength,
+ int MsgType)
+{
+ int i=0, nom=0, togo=0, thislength=0; /* number of messages, ... */
+ unsigned char buffer[FBUS2_MAX_TRANSMIT_LENGTH + 2]={0}, seqnum=0;
+ GSM_Protocol_FBUS2Data *d = &s->Protocol.Data.FBUS2;
+ GSM_Error error;
+
+ GSM_DumpMessageBinary(s, MsgBuffer, MsgLength, MsgType);
+
+ nom = (MsgLength + FBUS2_MAX_TRANSMIT_LENGTH - 1) / FBUS2_MAX_TRANSMIT_LENGTH;
+ togo = MsgLength;
+
+ for (i = 0; i < nom; i++) {
+ seqnum = d->MsgSequenceNumber;
+
+ if (i==0) {
+ seqnum = seqnum + 0x40;
+ }
+ d->MsgSequenceNumber = (d->MsgSequenceNumber + 1) & 0x07;
+
+ thislength = togo;
+
+ if (togo > FBUS2_MAX_TRANSMIT_LENGTH) {
+ thislength = FBUS2_MAX_TRANSMIT_LENGTH;
+ }
+ memcpy(buffer, MsgBuffer + (MsgLength - togo), thislength);
+ buffer[thislength] = nom - i;
+ buffer[thislength + 1] = seqnum;
+ togo = togo - thislength;
+
+ GSM_DumpMessageText(s, buffer, thislength, MsgType);
+
+ error = FBUS2_WriteFrame(s, buffer, thislength + 2, MsgType);
+
+ if (error != ERR_NONE) {
+ return error;
+ }
+ }
+ return ERR_NONE;
+}
+
+static GSM_Error FBUS2_SendAck(GSM_StateMachine *s,
+ unsigned char MsgType,
+ unsigned char MsgSequence)
+{
+ unsigned char buffer[2]={0};
+
+ buffer[0] = MsgType;
+ buffer[1] = MsgSequence;
+
+ smprintf_level(s, D_TEXT, "[Sending Ack of type %02x, seq %x]\n",
+ buffer[0],
+ buffer[1]);
+
+ /* Sending to phone */
+ return FBUS2_WriteFrame(s, buffer, 2, FBUS2_ACK_BYTE);
+}
+
+static GSM_Error FBUS2_StateMachine(GSM_StateMachine *s, unsigned char rx_char)
+{
+ GSM_Protocol_FBUS2Data *d = &s->Protocol.Data.FBUS2;
+ unsigned char frm_num, seq_num;
+ gboolean correct = FALSE;
+
+ /* XOR the byte with the earlier checksum */
+ d->Msg.CheckSum[d->Msg.Count & 1] ^= rx_char;
+
+ if (d->MsgRXState == RX_GetMessage) {
+ d->Msg.Buffer[d->Msg.Count] = rx_char;
+ d->Msg.Count++;
+
+ /* This is not last byte in frame */
+ if (d->Msg.Count != d->Msg.Length+(d->Msg.Length%2)+2) return ERR_NONE;
+
+ /* Checksum is incorrect */
+ if (d->Msg.CheckSum[0] != d->Msg.CheckSum[1]) {
+ smprintf_level(s, D_ERROR, "[ERROR: checksum]\n");
+ free(d->Msg.Buffer);
+ d->Msg.Buffer = NULL;
+ d->Msg.Length = 0;
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+
+ seq_num = d->Msg.Buffer[d->Msg.Length-1];
+
+ if (d->Msg.Type == FBUS2_ACK_BYTE) {
+ smprintf_level(s, D_TEXT, "[Received Ack of type %02x, seq %02x]\n",
+ d->Msg.Buffer[0], seq_num);
+ free(d->Msg.Buffer);
+ d->Msg.Buffer = NULL;
+ d->Msg.Length = 0;
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+
+ frm_num = d->Msg.Buffer[d->Msg.Length-2];
+
+ if ((seq_num & 0x40) == 0x40) {
+ d->FramesToGo = frm_num;
+ d->MultiMsg.Length = 0;
+ d->MultiMsg.Type = d->Msg.Type;
+ d->MultiMsg.Destination = d->Msg.Destination;
+ d->MultiMsg.Source = d->Msg.Source;
+ }
+
+ if ((seq_num & 0x40) != 0x40 && d->FramesToGo != frm_num) {
+ smprintf_level(s, D_ERROR, "[ERROR: Missed part of multiframe msg]\n");
+ free(d->Msg.Buffer);
+ d->Msg.Buffer = NULL;
+ d->Msg.Length = 0;
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+
+ if ((seq_num & 0x40) != 0x40 && d->Msg.Type != d->MultiMsg.Type) {
+ smprintf_level(s, D_ERROR, "[ERROR: Multiframe msg in multiframe msg]\n");
+ free(d->Msg.Buffer);
+ d->Msg.Buffer = NULL;
+ d->Msg.Length = 0;
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+
+ if (d->MultiMsg.BufferUsed < d->MultiMsg.Length+d->Msg.Length-2) {
+ d->MultiMsg.BufferUsed = d->MultiMsg.Length+d->Msg.Length-2;
+ d->MultiMsg.Buffer = (unsigned char *)realloc(d->MultiMsg.Buffer,d->MultiMsg.BufferUsed);
+ }
+ memcpy(d->MultiMsg.Buffer+d->MultiMsg.Length,d->Msg.Buffer,d->Msg.Length-2);
+ d->MultiMsg.Length = d->MultiMsg.Length+d->Msg.Length-2;
+
+ free(d->Msg.Buffer);
+ d->Msg.Buffer = NULL;
+ d->Msg.Length = 0;
+ d->FramesToGo--;
+
+ /* do not ack debug trace, as this could generate a
+ * (feedback loop) flood of which even Noah would be scared.
+ */
+ if (d->Msg.Type != 0) {
+ FBUS2_SendAck(s,d->Msg.Type,((unsigned char)(seq_num & 0x0f)));
+ }
+
+ if (d->FramesToGo == 0) {
+ s->Phone.Data.RequestMsg = &d->MultiMsg;
+ s->Phone.Data.DispatchError = s->Phone.Functions->DispatchMessage(s);
+ }
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+ if (d->MsgRXState == RX_GetLength2) {
+ d->Msg.Length = d->Msg.Length + rx_char;
+ d->Msg.Buffer = (unsigned char *)malloc(d->Msg.Length+3);
+ if (d->Msg.Buffer == NULL) {
+ return ERR_MOREMEMORY;
+ }
+ d->MsgRXState = RX_GetMessage;
+ return ERR_NONE;
+ }
+ if (d->MsgRXState == RX_GetLength1) {
+ d->Msg.Length = rx_char * 256;
+ d->MsgRXState = RX_GetLength2;
+ return ERR_NONE;
+ }
+ if (d->MsgRXState == RX_GetType) {
+ d->Msg.Type = rx_char;
+ d->MsgRXState = RX_GetLength1;
+ return ERR_NONE;
+ }
+ if (d->MsgRXState == RX_GetSource) {
+ if (rx_char != FBUS2_DEVICE_PHONE) {
+ smprintf_level(s, D_ERROR, "[ERROR: incorrect char - %02x, not %02x]\n",
+ rx_char, FBUS2_DEVICE_PHONE);
+
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+ d->Msg.Source = rx_char;
+
+ d->MsgRXState = RX_GetType;
+ return ERR_NONE;
+ }
+ if (d->MsgRXState == RX_GetDestination) {
+ if (rx_char != FBUS2_DEVICE_PC) {
+ smprintf_level(s, D_ERROR, "[ERROR: incorrect char - %02x, not %02x]\n",
+ rx_char, FBUS2_DEVICE_PC);
+
+ d->MsgRXState = RX_Sync;
+ return ERR_NONE;
+ }
+ d->Msg.Destination = rx_char;
+
+ d->MsgRXState = RX_GetSource;
+ return ERR_NONE;
+ }
+ if (d->MsgRXState == RX_Sync) {
+ switch (s->ConnectionType) {
+ case GCT_FBUS2:
+ case GCT_FBUS2DLR3:
+ case GCT_DKU5FBUS2:
+ case GCT_FBUS2PL2303:
+ case GCT_FBUS2BLUE:
+ case GCT_BLUEFBUS2:
+ if (rx_char == FBUS2_FRAME_ID) correct = TRUE;
+ break;
+ case GCT_FBUS2IRDA:
+ if (rx_char == FBUS2_IRDA_FRAME_ID) correct = TRUE;
+ break;
+ default:
+ break;
+ }
+ if (!correct) {
+ smprintf_level(s, D_ERROR, "[ERROR: incorrect char - %02x, not %02x]\n",
+ rx_char,
+ (s->ConnectionType == GCT_FBUS2IRDA) ? FBUS2_IRDA_FRAME_ID : FBUS2_FRAME_ID);
+ if (rx_char == 0x20) {
+ smprintf(s, "0x20 reply detected...\n");
+ smprintf(s, "Trying to reinit connection...\n");
+ FBUS2_Initialise(s);
+ }
+ return ERR_NONE;
+ }
+
+ d->Msg.CheckSum[0] = rx_char;
+ d->Msg.CheckSum[1] = 0;
+ d->Msg.Count = 0;
+
+ d->MsgRXState = RX_GetDestination;
+ return ERR_NONE;
+ }
+ return ERR_NONE;
+}
+
+#if defined(GSM_ENABLE_FBUS2DLR3) || defined(GSM_ENABLE_DKU5FBUS2) || defined(GSM_ENABLE_FBUS2BLUE) || defined(GSM_ENABLE_BLUEFBUS2) || defined(GSM_ENABLE_FBUS2PL2303)
+/**
+ * Writes (AT) command to device and reads reply.
+ *
+ * \todo This makes no reply parsing or error detection.
+ */
+static void FBUS2_WriteDLR3(GSM_StateMachine *s, const char *command, int length, int timeout)
+{
+ unsigned char buff[300]={0};
+ int w = 0,recvlen=0;
+ gboolean wassomething = FALSE;
+
+ GSM_DumpMessageText(s, command, length, 0xff);
+ s->Device.Functions->WriteDevice(s, command, length);
+
+ for (w = 0; w < timeout; w++) {
+ recvlen = s->Device.Functions->ReadDevice(s, buff, sizeof(buff));
+
+ if (wassomething && recvlen == 0) {
+ return;
+ } else if (recvlen > 0) {
+ GSM_DumpMessageTextRecv(s, buff, recvlen, 0xff);
+ wassomething = TRUE;
+ }
+ usleep(50000);
+ }
+}
+
+/**
+ * Performs switch to FBUS2 protocol using AT commands.
+ *
+ * \todo We should check return codes here.
+ */
+static GSM_Error FBUS2_ATSwitch(GSM_StateMachine *s)
+{
+ static const char init_1[] = "AT\r\n";
+ static const char init_2[] = "AT&F\r\n";
+ static const char init_3[] = "AT*NOKIAFBUS\r\n";
+
+ smprintf(s, "Switching to FBUS using AT commands\n");
+
+ FBUS2_WriteDLR3(s, init_1, strlen(init_1), 100);
+ FBUS2_WriteDLR3(s, init_2, strlen(init_2), 100);
+ FBUS2_WriteDLR3(s, init_3, strlen(init_3), 100);
+
+ return ERR_NONE;
+}
+#endif
+
+/**
+ * Performs initial synchronisation of FBUS2.
+ */
+static GSM_Error FBUS2_InitSequence(GSM_StateMachine *s, const int repeats, const int delays, const gboolean terminate)
+{
+ int count=0,write_data=0;
+ static const unsigned char init_char = 0x55;
+ static const unsigned char end_init_char = 0xc1;
+
+ for (count = 0; count < repeats; count ++) {
+ write_data=s->Device.Functions->WriteDevice(s, &init_char, 1);
+
+ if (write_data != 1) {
+ return ERR_DEVICEWRITEERROR;
+ }
+
+ if (delays > 0) {
+ usleep(delays);
+ }
+ }
+
+ if (terminate) {
+ write_data=s->Device.Functions->WriteDevice(s, &end_init_char, 1);
+
+ if (write_data != 1) {
+ return ERR_DEVICEWRITEERROR;
+ }
+ }
+ sleep(1);
+ return ERR_NONE;
+}
+
+static GSM_Error FBUS2_Initialise(GSM_StateMachine *s)
+{
+ GSM_Protocol_FBUS2Data *d = &s->Protocol.Data.FBUS2;
+ GSM_Device_Functions *Device = s->Device.Functions;
+ GSM_Error error;
+ unsigned char buff[300]={0};
+
+ d->Msg.Length = 0;
+ d->Msg.Buffer = NULL;
+ d->MultiMsg.BufferUsed = 0;
+ d->MultiMsg.Length = 0;
+ d->MultiMsg.Buffer = NULL;
+
+ d->MsgSequenceNumber = 0;
+ d->FramesToGo = 0;
+ d->MsgRXState = RX_Sync;
+
+ error = Device->DeviceSetParity(s, FALSE);
+ if (error != ERR_NONE) return error;
+
+ switch (s->ConnectionType) {
+#if defined(GSM_ENABLE_BLUEFBUS2) || defined(GSM_ENABLE_FBUS2BLUE)
+ case GCT_FBUS2BLUE:
+ case GCT_BLUEFBUS2:
+ error = FBUS2_ATSwitch(s);
+ if (error != ERR_NONE) return error;
+ break;
+#endif
+#if defined(GSM_ENABLE_FBUS2DLR3) || defined(GSM_ENABLE_DKU5FBUS2) || defined(GSM_ENABLE_FBUS2PL2303)
+ case GCT_DKU5FBUS2:
+ case GCT_FBUS2PL2303:
+ case GCT_FBUS2DLR3:
+ error = Device->DeviceSetDtrRts(s,FALSE,FALSE);
+ if (error != ERR_NONE) return error;
+ sleep(1);
+
+ if (! s->NoPowerCable) {
+ error = Device->DeviceSetDtrRts(s,TRUE,TRUE);
+ if (error != ERR_NONE) return error;
+ sleep(1);
+ }
+
+ error = Device->DeviceSetSpeed(s,19200);
+ if (error != ERR_NONE) return error;
+
+ error = FBUS2_ATSwitch(s);
+ if (error != ERR_NONE) return error;
+
+ error = Device->DeviceSetSpeed(s,115200);
+ if (error != ERR_NONE) return error;
+
+ error = FBUS2_InitSequence(s, 32, 0, TRUE);
+ if (error != ERR_NONE) return error;
+
+ break;
+#endif
+ case GCT_FBUS2:
+ error = Device->DeviceSetSpeed(s,115200);
+ if (error != ERR_NONE) return error;
+
+ /* Set DTR as power supply if needed, RTS is always low */
+ error = Device->DeviceSetDtrRts(s, !(s->NoPowerCable), FALSE);
+ if (error != ERR_NONE) return error;
+
+ error = FBUS2_InitSequence(s, 32, 0, TRUE);
+ if (error != ERR_NONE) return error;
+
+ break;
+#ifdef GSM_ENABLE_FBUS2IRDA
+ case GCT_FBUS2IRDA:
+ error = Device->DeviceSetSpeed(s,9600);
+ if (error != ERR_NONE) return error;
+
+ error = FBUS2_InitSequence(s, 32, 0, TRUE);
+ if (error != ERR_NONE) return error;
+
+ error = Device->DeviceSetSpeed(s,115200);
+ if (error != ERR_NONE) return error;
+
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /* A bit more of synchronisation could be needed here */
+ if (s->ConnectionType != GCT_FBUS2BLUE && s->ConnectionType != GCT_BLUEFBUS2) {
+ error = FBUS2_InitSequence(s, 250, 100, FALSE);
+ if (error != ERR_NONE) return error;
+ }
+
+ /* Read any possible junk on the line */
+ while (s->Device.Functions->ReadDevice(s, buff, sizeof(buff)) > 0) {
+ usleep(1000);
+ }
+
+ return ERR_NONE;
+}
+
+static GSM_Error FBUS2_Terminate(GSM_StateMachine *s)
+{
+ free(s->Protocol.Data.FBUS2.Msg.Buffer);
+ s->Protocol.Data.FBUS2.Msg.Buffer = NULL;
+ free(s->Protocol.Data.FBUS2.MultiMsg.Buffer);
+ s->Protocol.Data.FBUS2.MultiMsg.Buffer = NULL;
+ sleep(2);
+ return ERR_NONE;
+}
+
+GSM_Protocol_Functions FBUS2Protocol = {
+ FBUS2_WriteMessage,
+ FBUS2_StateMachine,
+ FBUS2_Initialise,
+ FBUS2_Terminate
+};
+
+#endif
+
+/* How should editor hadle tabs in this file? Add editor commands here.
+ * vim: noexpandtab sw=8 ts=8 sts=8:
+ */