7 A Linux/Unix toolset and driver for Nokia mobile phones.
9 Copyright (C) 2000 Hugh Blemings & Pavel JanÃk ml.
10 Copyright (C) 2000 Chris Kemp
12 Released under the terms of the GNU GPL, see file COPYING for more details.
14 This file provides an API for accessing functions via fbus.
15 See README for more details on supported mobile phones.
17 The various routines are called FBUS_(whatever).
20 Revision 1.1.1.2 2001/11/27 22:01:16 short
21 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Tue Nov 27 22:58 CET 2001
23 Revision 1.14 2001/11/27 12:19:01 pkot
24 Cleanup, indentation, ANSI complaint preprocesor symbols (Jan Kratochvil, me)
26 Revision 1.13 2001/11/17 20:14:15 pkot
27 Nasty bug with counting message length. Workaround applied. Needs fixing.
29 Revision 1.12 2001/11/15 12:04:05 pkot
30 Faster initialization for 6100 series (don't check for dlr3 cable)
32 Revision 1.11 2001/09/09 21:45:49 machek
33 Cleanups from Ladislav Michl <ladis@psi.cz>:
35 *) do *not* internationalize debug messages
37 *) some whitespace fixes, do not use //
39 *) break is unneccessary after return
41 Revision 1.10 2001/08/20 23:27:37 pkot
42 Add hardware shakehand to the link layer (Manfred Jonsson)
44 Revision 1.9 2001/05/28 09:25:16 pkot
45 Fixed autodetecting of the cable type in 6110 and 7110 series. DLR-3 is
46 tried first now. Seems to work ok with either 6130 or 6210.
48 Revision 1.8 2001/05/07 16:24:03 pkot
49 DLR-3P temporary fix. How should I do it better?
51 Revision 1.7 2001/03/22 16:17:05 chris
52 Tidy-ups and fixed gnokii/Makefile and gnokii/ChangeLog which I somehow corrupted.
54 Revision 1.6 2001/03/21 23:36:05 chris
55 Added the statemachine
56 This will break gnokii --identify and --monitor except for 6210/7110
58 Revision 1.5 2001/03/19 23:44:56 pkot
61 Revision 1.4 2001/03/13 01:24:02 pkot
62 Code cleanup - no warnings during compilation
64 Revision 1.3 2001/03/13 01:23:18 pkot
65 Windows updates (Manfred Jonsson)
67 Revision 1.2 2001/03/11 11:18:39 machek
68 Made fbus link_dispatch (and fixed minor memory leak)
70 Revision 1.1 2001/02/21 19:57:06 chris
71 More fiddling with the directory layout
73 Revision 1.1 2001/02/16 14:29:52 chris
74 Restructure of common/. Fixed a problem in fbus-phonet.c
75 Lots of dprintfs for Marcin
76 Any size xpm can now be loaded (eg for 7110 startup logos)
77 nk7110 code detects 7110/6210 and alters startup logo size to suit
78 Moved Marcin's extended phonebook code into gnokii.c
80 Revision 1.3 2001/02/06 21:15:34 chris
81 Preliminary irda support for 7110 etc. Not well tested!
83 Revision 1.2 2001/02/03 23:56:14 chris
84 Start of work on irda support (now we just need fbus-irda.c!)
85 Proper unicode support in 7110 code (from pkot)
87 Revision 1.1 2001/01/14 22:46:59 chris
88 Preliminary 7110 support (dlr9 only) and the beginnings of a new structure
93 /* System header files */
99 /* Various header file */
103 #include "gsm-common.h"
104 #include "gsm-ringtones.h"
105 #include "gsm-networks.h"
106 #include "gsm-statemachine.h"
107 #include "links/utils.h"
112 # include "win32/winserial.h"
113 # define device_write(a, b) WriteCommBlock(a, b)
114 # define device_read(a, b) ReadCommBlock(a, b)
115 # define sleep(x) Sleep((x) * 1000)
116 # define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
119 #define __links_fbus_c
120 #include "links/fbus.h"
122 /* FIXME - pass device_* the link stuff?? */
123 /* FIXME - win32 stuff! */
128 static GSM_Link *glink;
129 static GSM_Statemachine *statemachine;
130 static FBUS_Link flink; /* FBUS specific stuff, internal to this file */
133 /*--------------------------------------------*/
135 bool FBUS_OpenSerial(bool dlr3)
140 if (OpenConnection(glink->PortDevice, FBUS_RX_StateMachine, NULL)) {
142 if (!device_open(glink->PortDevice, false, false, false, GCT_Serial)) {
144 perror(_("Couldn't open FBUS device"));
147 device_changespeed(115200);
149 /* clearing the RTS bit and setting the DTR bit */
150 device_setdtrrts((1-dlr3), 0);
156 /* RX_State machine for receive handling. Called once for each character
157 received from the phone. */
159 void FBUS_RX_StateMachine(unsigned char rx_byte)
162 struct timeval time_diff;
163 FBUS_IncomingFrame *i = &flink.i;
164 int frm_num, seq_num;
165 FBUS_IncomingMessage *m;
168 if (isprint(rx_byte))
169 dprintf("[%02x%c]", (unsigned char) rx_byte, rx_byte);
171 dprintf("[%02x ]", (unsigned char) rx_byte);
174 /* XOR the byte with the current checksum */
175 i->checksum[i->BufferCount & 1] ^= rx_byte;
179 /* Messages from the phone start with an 0x1e (cable) or 0x1c (IR).
180 We use this to "synchronise" with the incoming data stream. However,
181 if we see something else, we assume we have lost sync and we require
182 a gap of at least 5ms before we start looking again. This is because
183 the data part of the frame could contain a byte which looks like the
186 case FBUS_RX_Discarding:
187 gettimeofday(&i->time_now, NULL);
188 timersub(&i->time_now, &i->time_last, &time_diff);
189 if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
190 i->time_last = i->time_now; /* no gap seen, continue discarding */
194 /* else fall through to... */
197 if (glink->ConnectionType == GCT_Infrared) {
198 if (rx_byte == FBUS_IR_FRAME_ID) {
199 /* Initialize checksums. */
200 i->checksum[0] = FBUS_IR_FRAME_ID;
202 i->state = FBUS_RX_GetDestination;
204 /* Lost frame sync */
205 i->state = FBUS_RX_Discarding;
206 gettimeofday(&i->time_last, NULL);
209 } else { /* glink->ConnectionType == GCT_Serial */
210 if (rx_byte == FBUS_FRAME_ID) {
211 /* Initialize checksums. */
212 i->checksum[0] = FBUS_FRAME_ID;
214 i->state = FBUS_RX_GetDestination;
216 /* Lost frame sync */
217 i->state = FBUS_RX_Discarding;
218 gettimeofday(&i->time_last, NULL);
224 case FBUS_RX_GetDestination:
226 i->MessageDestination = rx_byte;
227 i->state = FBUS_RX_GetSource;
229 /* When there is a checksum error and things get out of sync we have to manage to resync */
230 /* If doing a data call at the time, finding a 0x1e etc is really quite likely in the data stream */
231 /* Then all sorts of horrible things happen because the packet length etc is wrong... */
232 /* Therefore we test here for a destination of 0x0c and return to the top if it is not */
234 if (rx_byte != 0x0c) {
235 i->state = FBUS_RX_Sync;
236 dprintf("The fbus stream is out of sync - expected 0x0c, got %2x\n", rx_byte);
241 case FBUS_RX_GetSource:
243 i->MessageSource = rx_byte;
244 i->state = FBUS_RX_GetType;
246 /* Source should be 0x00 */
248 if (rx_byte != 0x00) {
249 i->state = FBUS_RX_Sync;
250 dprintf("The fbus stream is out of sync - expected 0x00, got %2x\n",rx_byte);
255 case FBUS_RX_GetType:
257 i->MessageType = rx_byte;
258 i->state = FBUS_RX_GetLength1;
262 case FBUS_RX_GetLength1:
264 i->FrameLength = rx_byte << 8;
265 i->state = FBUS_RX_GetLength2;
269 case FBUS_RX_GetLength2:
271 i->FrameLength = i->FrameLength + rx_byte;
272 i->state = FBUS_RX_GetMessage;
277 case FBUS_RX_GetMessage:
279 i->MessageBuffer[i->BufferCount] = rx_byte;
282 if (i->BufferCount > FBUS_MAX_FRAME_LENGTH) {
283 dprintf("FBUS: Message buffer overun - resetting\n");
284 i->state = FBUS_RX_Sync;
288 /* If this is the last byte, it's the checksum. */
290 if (i->BufferCount == i->FrameLength + (i->FrameLength % 2) + 2) {
291 /* Is the checksum correct? */
292 if (i->checksum[0] == i->checksum[1]) {
294 /* Deal with exceptions to the rules - acks and rlp.. */
296 if (i->MessageType == 0x7f) {
297 dprintf("[Received Ack of type %02x, seq: %2x]\n",
298 i->MessageBuffer[0],(unsigned char) i->MessageBuffer[1]);
300 } else { /* Normal message type */
302 /* Add data to the relevant Message buffer */
303 /* having checked the sequence number */
305 m = &flink.messages[i->MessageType];
307 frm_num = i->MessageBuffer[i->FrameLength - 2];
308 seq_num = i->MessageBuffer[i->FrameLength - 1];
311 /* 0x40 in the sequence number indicates first frame of a message */
313 if ((seq_num & 0x40) == 0x40) {
314 /* Fiddle around and malloc some memory */
315 m->MessageLength = 0;
316 m->FramesToGo = frm_num;
317 if (m->Malloced != 0) {
318 free(m->MessageBuffer);
320 m->MessageBuffer = NULL;
322 m->Malloced = frm_num * m->MessageLength;
323 m->MessageBuffer = (char *) malloc(m->Malloced);
325 } else if (m->FramesToGo != frm_num) {
326 dprintf("Missed a frame in a multiframe message.\n");
327 /* FIXME - we should make sure we don't ack the rest etc */
330 if (m->Malloced < m->MessageLength + i->FrameLength) {
331 m->Malloced = m->MessageLength + i->FrameLength;
332 m->MessageBuffer = (char *) realloc(m->MessageBuffer, m->Malloced);
335 memcpy(m->MessageBuffer + m->MessageLength, i->MessageBuffer,
336 i->FrameLength - 2);/* - (i->FrameLength % 2)); */
338 m->MessageLength += i->FrameLength - 2;/* - (i->FrameLength % 2); */
342 /* Finally dispatch if ready */
344 if (m->FramesToGo == 0) {
345 SM_IncomingFunction(statemachine, i->MessageType, m->MessageBuffer, m->MessageLength);
346 free(m->MessageBuffer);
347 m->MessageBuffer = NULL;
351 /* Send an ack (for all for now) */
353 FBUS_TX_SendAck(i->MessageType, seq_num & 0x0f);
357 dprintf("Bad checksum!\n");
359 i->state = FBUS_RX_Sync;
366 /* This is the main loop function which must be called regularly */
367 /* timeout can be used to make it 'busy' or not */
369 GSM_Error FBUS_Loop(struct timeval *timeout)
372 unsigned char buffer[255];
375 res = device_select(timeout);
377 res = device_read(buffer, 255);
378 for (count = 0; count < res; count++)
379 FBUS_RX_StateMachine(buffer[count]);
383 /* This traps errors from device_read */
387 return GE_INTERNALERROR;
395 /* Prepares the message header and sends it, prepends the message start byte
396 (0x1e) and other values according the value specified when called.
397 Calculates checksum and then sends the lot down the pipe... */
399 int FBUS_TX_SendFrame(u8 message_length, u8 message_type, u8 * buffer)
402 u8 out_buffer[FBUS_MAX_TRANSMIT_LENGTH + 5];
403 int count, current = 0;
404 unsigned char checksum;
406 /* FIXME - we should check for the message length ... */
408 /* Now construct the message header. */
410 if (glink->ConnectionType == GCT_Infrared)
411 out_buffer[current++] = FBUS_IR_FRAME_ID; /* Start of the IR frame indicator */
412 else /* ConnectionType == GCT_Serial */
413 out_buffer[current++] = FBUS_FRAME_ID; /* Start of the frame indicator */
415 out_buffer[current++] = FBUS_DEVICE_PHONE; /* Destination */
416 out_buffer[current++] = FBUS_DEVICE_PC; /* Source */
418 out_buffer[current++] = message_type; /* Type */
420 out_buffer[current++] = 0; /* Length */
422 out_buffer[current++] = message_length; /* Length */
424 /* Copy in data if any. */
426 if (message_length != 0) {
427 memcpy(out_buffer + current, buffer, message_length);
428 current += message_length;
431 /* If the message length is odd we should add pad byte 0x00 */
432 if (message_length % 2)
433 out_buffer[current++] = 0x00;
435 /* Now calculate checksums over entire message and append to message. */
440 for (count = 0; count < current; count += 2)
441 checksum ^= out_buffer[count];
443 out_buffer[current++] = checksum;
448 for (count = 1; count < current; count += 2)
449 checksum ^= out_buffer[count];
451 out_buffer[current++] = checksum;
454 fprintf(stderr, _("PC: "));
456 for (count = 0; count < current; count++)
457 fprintf(stderr, "%02x:", out_buffer[count]);
459 fprintf(stderr, "\n");
464 if (device_write(out_buffer, current) != current)
471 /* Main function to send an fbus message */
472 /* Splits up the message into frames if necessary */
474 GSM_Error FBUS_SendMessage(u16 messagesize, u8 messagetype, void *message)
477 u8 seqnum, frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
478 u8 nom, lml; /* number of messages, last message len */
481 seqnum = 0x40 + flink.RequestSequenceNumber;
482 flink.RequestSequenceNumber =
483 (flink.RequestSequenceNumber + 1) & 0x07;
485 if (messagesize > FBUS_MAX_CONTENT_LENGTH) {
487 nom = (messagesize + FBUS_MAX_CONTENT_LENGTH - 1)
488 / FBUS_MAX_CONTENT_LENGTH;
489 lml = messagesize - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
491 for (i = 0; i < nom - 1; i++) {
493 memcpy(frame_buffer, message + (i * FBUS_MAX_CONTENT_LENGTH),
494 FBUS_MAX_CONTENT_LENGTH);
495 frame_buffer[FBUS_MAX_CONTENT_LENGTH] = nom - i;
496 frame_buffer[FBUS_MAX_CONTENT_LENGTH + 1] = seqnum;
498 FBUS_TX_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2,
499 messagetype, frame_buffer);
501 seqnum = flink.RequestSequenceNumber;
502 flink.RequestSequenceNumber = (flink.RequestSequenceNumber + 1) & 0x07;
505 memcpy(frame_buffer, message + ((nom - 1) * FBUS_MAX_CONTENT_LENGTH), lml);
506 frame_buffer[lml] = 0x01;
507 frame_buffer[lml + 1] = seqnum;
508 FBUS_TX_SendFrame(lml + 2, messagetype, frame_buffer);
512 memcpy(frame_buffer, message, messagesize);
513 frame_buffer[messagesize] = 0x01;
514 frame_buffer[messagesize + 1] = seqnum;
515 FBUS_TX_SendFrame(messagesize + 2, messagetype,
522 int FBUS_TX_SendAck(u8 message_type, u8 message_seq)
525 unsigned char request[2];
527 request[0] = message_type;
528 request[1] = message_seq;
530 dprintf("[Sending Ack of type %02x, seq: %x]\n",message_type, message_seq);
532 return FBUS_TX_SendFrame(2, 0x7f, request);
536 /* Initialise variables and start the link */
537 /* newlink is actually part of state - but the link code should not anything about state */
538 /* state is only passed around to allow for muliple state machines (one day...) */
540 GSM_Error FBUS_Initialise(GSM_Link *newlink, GSM_Statemachine *state, int type)
542 unsigned char init_char = 0x55;
545 if (type > 2) return GE_DEVICEOPENFAILED;
546 /* 'Copy in' the global structures */
548 statemachine = state;
550 /* Fill in the link functions */
551 glink->Loop = &FBUS_Loop;
552 glink->SendMessage = &FBUS_SendMessage;
554 /* Check for a valid init length */
555 if (glink->InitLength == 0)
556 glink->InitLength = 250;
558 /* Start up the link */
559 flink.RequestSequenceNumber = 0;
561 if (glink->ConnectionType == GCT_Infrared) {
563 return GE_DEVICEOPENFAILED;
564 } else { /* ConnectionType == GCT_Serial */
565 /* FBUS_OpenSerial(0) - try dau-9p
566 * FBUS_OpenSerial(n != 0) - try dlr-3p */
567 if (!FBUS_OpenSerial(type))
568 return GE_DEVICEOPENFAILED;
571 /* Send init string to phone, this is a bunch of 0x55 characters. Timing is
573 /* I believe that we need/can do this for any phone to get the UART synced */
575 for (count = 0; count < glink->InitLength; count++) {
577 device_write(&init_char, 1);
581 flink.i.state = FBUS_RX_Sync;
582 flink.i.BufferCount = 0;
583 for (count = 0; count < FBUS_MAX_MESSAGE_TYPES; count++) {
584 flink.messages[count].Malloced = 0;
585 flink.messages[count].FramesToGo = 0;
586 flink.messages[count].MessageLength = 0;