/* $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.2.1 2001/11/27 22:48:37 short Update: orig2001_11_27_05_17 -> orig2001_11_27_22_58 Revision 1.1.1.2 2001/11/27 22:01:16 short :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 22:58 CET 2001 Revision 1.14 2001/11/27 12:19:01 pkot Cleanup, indentation, ANSI complaint preprocesor symbols (Jan Kratochvil, me) 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 : *) 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 #include #include /* 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; }