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.1 2001/11/25 21:59:10 short
21 :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
23 Revision 1.13 2001/11/17 20:14:15 pkot
24 Nasty bug with counting message length. Workaround applied. Needs fixing.
26 Revision 1.12 2001/11/15 12:04:05 pkot
27 Faster initialization for 6100 series (don't check for dlr3 cable)
29 Revision 1.11 2001/09/09 21:45:49 machek
30 Cleanups from Ladislav Michl <ladis@psi.cz>:
32 *) do *not* internationalize debug messages
34 *) some whitespace fixes, do not use //
36 *) break is unneccessary after return
38 Revision 1.10 2001/08/20 23:27:37 pkot
39 Add hardware shakehand to the link layer (Manfred Jonsson)
41 Revision 1.9 2001/05/28 09:25:16 pkot
42 Fixed autodetecting of the cable type in 6110 and 7110 series. DLR-3 is
43 tried first now. Seems to work ok with either 6130 or 6210.
45 Revision 1.8 2001/05/07 16:24:03 pkot
46 DLR-3P temporary fix. How should I do it better?
48 Revision 1.7 2001/03/22 16:17:05 chris
49 Tidy-ups and fixed gnokii/Makefile and gnokii/ChangeLog which I somehow corrupted.
51 Revision 1.6 2001/03/21 23:36:05 chris
52 Added the statemachine
53 This will break gnokii --identify and --monitor except for 6210/7110
55 Revision 1.5 2001/03/19 23:44:56 pkot
58 Revision 1.4 2001/03/13 01:24:02 pkot
59 Code cleanup - no warnings during compilation
61 Revision 1.3 2001/03/13 01:23:18 pkot
62 Windows updates (Manfred Jonsson)
64 Revision 1.2 2001/03/11 11:18:39 machek
65 Made fbus link_dispatch (and fixed minor memory leak)
67 Revision 1.1 2001/02/21 19:57:06 chris
68 More fiddling with the directory layout
70 Revision 1.1 2001/02/16 14:29:52 chris
71 Restructure of common/. Fixed a problem in fbus-phonet.c
72 Lots of dprintfs for Marcin
73 Any size xpm can now be loaded (eg for 7110 startup logos)
74 nk7110 code detects 7110/6210 and alters startup logo size to suit
75 Moved Marcin's extended phonebook code into gnokii.c
77 Revision 1.3 2001/02/06 21:15:34 chris
78 Preliminary irda support for 7110 etc. Not well tested!
80 Revision 1.2 2001/02/03 23:56:14 chris
81 Start of work on irda support (now we just need fbus-irda.c!)
82 Proper unicode support in 7110 code (from pkot)
84 Revision 1.1 2001/01/14 22:46:59 chris
85 Preliminary 7110 support (dlr9 only) and the beginnings of a new structure
90 /* System header files */
96 /* Various header file */
100 #include "gsm-common.h"
101 #include "gsm-ringtones.h"
102 #include "gsm-networks.h"
103 #include "gsm-statemachine.h"
104 #include "links/utils.h"
109 #include "win32/winserial.h"
110 #define device_write(a, b) WriteCommBlock(a, b)
111 #define device_read(a, b) ReadCommBlock(a, b)
112 #define sleep(x) Sleep((x) * 1000)
113 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
116 #define __links_fbus_c
117 #include "links/fbus.h"
119 /* FIXME - pass device_* the link stuff?? */
120 /* FIXME - win32 stuff! */
125 static GSM_Link *glink;
126 static GSM_Statemachine *statemachine;
127 static FBUS_Link flink; /* FBUS specific stuff, internal to this file */
130 /*--------------------------------------------*/
132 bool FBUS_OpenSerial(bool dlr3)
137 if (OpenConnection(glink->PortDevice, FBUS_RX_StateMachine, NULL)) {
139 if (!device_open(glink->PortDevice, false, false, false, GCT_Serial)) {
141 perror(_("Couldn't open FBUS device"));
144 device_changespeed(115200);
146 /* clearing the RTS bit and setting the DTR bit */
147 device_setdtrrts((1-dlr3), 0);
153 /* RX_State machine for receive handling. Called once for each character
154 received from the phone. */
156 void FBUS_RX_StateMachine(unsigned char rx_byte)
159 struct timeval time_diff;
160 FBUS_IncomingFrame *i = &flink.i;
161 int frm_num, seq_num;
162 FBUS_IncomingMessage *m;
165 if (isprint(rx_byte))
166 dprintf("[%02x%c]", (unsigned char) rx_byte, rx_byte);
168 dprintf("[%02x ]", (unsigned char) rx_byte);
171 /* XOR the byte with the current checksum */
172 i->checksum[i->BufferCount & 1] ^= rx_byte;
176 /* Messages from the phone start with an 0x1e (cable) or 0x1c (IR).
177 We use this to "synchronise" with the incoming data stream. However,
178 if we see something else, we assume we have lost sync and we require
179 a gap of at least 5ms before we start looking again. This is because
180 the data part of the frame could contain a byte which looks like the
183 case FBUS_RX_Discarding:
184 gettimeofday(&i->time_now, NULL);
185 timersub(&i->time_now, &i->time_last, &time_diff);
186 if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
187 i->time_last = i->time_now; /* no gap seen, continue discarding */
191 /* else fall through to... */
194 if (glink->ConnectionType == GCT_Infrared) {
195 if (rx_byte == FBUS_IR_FRAME_ID) {
196 /* Initialize checksums. */
197 i->checksum[0] = FBUS_IR_FRAME_ID;
199 i->state = FBUS_RX_GetDestination;
201 /* Lost frame sync */
202 i->state = FBUS_RX_Discarding;
203 gettimeofday(&i->time_last, NULL);
206 } else { /* glink->ConnectionType == GCT_Serial */
207 if (rx_byte == FBUS_FRAME_ID) {
208 /* Initialize checksums. */
209 i->checksum[0] = FBUS_FRAME_ID;
211 i->state = FBUS_RX_GetDestination;
213 /* Lost frame sync */
214 i->state = FBUS_RX_Discarding;
215 gettimeofday(&i->time_last, NULL);
221 case FBUS_RX_GetDestination:
223 i->MessageDestination = rx_byte;
224 i->state = FBUS_RX_GetSource;
226 /* When there is a checksum error and things get out of sync we have to manage to resync */
227 /* If doing a data call at the time, finding a 0x1e etc is really quite likely in the data stream */
228 /* Then all sorts of horrible things happen because the packet length etc is wrong... */
229 /* Therefore we test here for a destination of 0x0c and return to the top if it is not */
231 if (rx_byte != 0x0c) {
232 i->state = FBUS_RX_Sync;
233 dprintf("The fbus stream is out of sync - expected 0x0c, got %2x\n", rx_byte);
238 case FBUS_RX_GetSource:
240 i->MessageSource = rx_byte;
241 i->state = FBUS_RX_GetType;
243 /* Source should be 0x00 */
245 if (rx_byte != 0x00) {
246 i->state = FBUS_RX_Sync;
247 dprintf("The fbus stream is out of sync - expected 0x00, got %2x\n",rx_byte);
252 case FBUS_RX_GetType:
254 i->MessageType = rx_byte;
255 i->state = FBUS_RX_GetLength1;
259 case FBUS_RX_GetLength1:
261 i->FrameLength = rx_byte << 8;
262 i->state = FBUS_RX_GetLength2;
266 case FBUS_RX_GetLength2:
268 i->FrameLength = i->FrameLength + rx_byte;
269 i->state = FBUS_RX_GetMessage;
274 case FBUS_RX_GetMessage:
276 i->MessageBuffer[i->BufferCount] = rx_byte;
279 if (i->BufferCount > FBUS_MAX_FRAME_LENGTH) {
280 dprintf("FBUS: Message buffer overun - resetting\n");
281 i->state = FBUS_RX_Sync;
285 /* If this is the last byte, it's the checksum. */
287 if (i->BufferCount == i->FrameLength + (i->FrameLength % 2) + 2) {
288 /* Is the checksum correct? */
289 if (i->checksum[0] == i->checksum[1]) {
291 /* Deal with exceptions to the rules - acks and rlp.. */
293 if (i->MessageType == 0x7f) {
294 dprintf("[Received Ack of type %02x, seq: %2x]\n",
295 i->MessageBuffer[0],(unsigned char) i->MessageBuffer[1]);
297 } else { /* Normal message type */
299 /* Add data to the relevant Message buffer */
300 /* having checked the sequence number */
302 m = &flink.messages[i->MessageType];
304 frm_num = i->MessageBuffer[i->FrameLength - 2];
305 seq_num = i->MessageBuffer[i->FrameLength - 1];
308 /* 0x40 in the sequence number indicates first frame of a message */
310 if ((seq_num & 0x40) == 0x40) {
311 /* Fiddle around and malloc some memory */
312 m->MessageLength = 0;
313 m->FramesToGo = frm_num;
314 if (m->Malloced != 0) {
315 free(m->MessageBuffer);
317 m->MessageBuffer = NULL;
319 m->Malloced = frm_num * m->MessageLength;
320 m->MessageBuffer = (char *) malloc(m->Malloced);
322 } else if (m->FramesToGo != frm_num) {
323 dprintf("Missed a frame in a multiframe message.\n");
324 /* FIXME - we should make sure we don't ack the rest etc */
327 if (m->Malloced < m->MessageLength + i->FrameLength) {
328 m->Malloced = m->MessageLength + i->FrameLength;
329 m->MessageBuffer = (char *) realloc(m->MessageBuffer, m->Malloced);
332 memcpy(m->MessageBuffer + m->MessageLength, i->MessageBuffer,
333 i->FrameLength - 2);/* - (i->FrameLength % 2)); */
335 m->MessageLength += i->FrameLength - 2;/* - (i->FrameLength % 2); */
339 /* Finally dispatch if ready */
341 if (m->FramesToGo == 0) {
342 SM_IncomingFunction(statemachine, i->MessageType, m->MessageBuffer, m->MessageLength);
343 free(m->MessageBuffer);
344 m->MessageBuffer = NULL;
348 /* Send an ack (for all for now) */
350 FBUS_TX_SendAck(i->MessageType, seq_num & 0x0f);
354 dprintf("Bad checksum!\n");
356 i->state = FBUS_RX_Sync;
363 /* This is the main loop function which must be called regularly */
364 /* timeout can be used to make it 'busy' or not */
366 GSM_Error FBUS_Loop(struct timeval *timeout)
369 unsigned char buffer[255];
372 res = device_select(timeout);
374 res = device_read(buffer, 255);
375 for (count = 0; count < res; count++)
376 FBUS_RX_StateMachine(buffer[count]);
380 /* This traps errors from device_read */
384 return GE_INTERNALERROR;
392 /* Prepares the message header and sends it, prepends the message start byte
393 (0x1e) and other values according the value specified when called.
394 Calculates checksum and then sends the lot down the pipe... */
396 int FBUS_TX_SendFrame(u8 message_length, u8 message_type, u8 * buffer)
399 u8 out_buffer[FBUS_MAX_TRANSMIT_LENGTH + 5];
400 int count, current = 0;
401 unsigned char checksum;
403 /* FIXME - we should check for the message length ... */
405 /* Now construct the message header. */
407 if (glink->ConnectionType == GCT_Infrared)
408 out_buffer[current++] = FBUS_IR_FRAME_ID; /* Start of the IR frame indicator */
409 else /* ConnectionType == GCT_Serial */
410 out_buffer[current++] = FBUS_FRAME_ID; /* Start of the frame indicator */
412 out_buffer[current++] = FBUS_DEVICE_PHONE; /* Destination */
413 out_buffer[current++] = FBUS_DEVICE_PC; /* Source */
415 out_buffer[current++] = message_type; /* Type */
417 out_buffer[current++] = 0; /* Length */
419 out_buffer[current++] = message_length; /* Length */
421 /* Copy in data if any. */
423 if (message_length != 0) {
424 memcpy(out_buffer + current, buffer, message_length);
425 current += message_length;
428 /* If the message length is odd we should add pad byte 0x00 */
429 if (message_length % 2)
430 out_buffer[current++] = 0x00;
432 /* Now calculate checksums over entire message and append to message. */
437 for (count = 0; count < current; count += 2)
438 checksum ^= out_buffer[count];
440 out_buffer[current++] = checksum;
445 for (count = 1; count < current; count += 2)
446 checksum ^= out_buffer[count];
448 out_buffer[current++] = checksum;
451 fprintf(stderr, _("PC: "));
453 for (count = 0; count < current; count++)
454 fprintf(stderr, "%02x:", out_buffer[count]);
456 fprintf(stderr, "\n");
461 if (device_write(out_buffer, current) != current)
468 /* Main function to send an fbus message */
469 /* Splits up the message into frames if necessary */
471 GSM_Error FBUS_SendMessage(u16 messagesize, u8 messagetype, void *message)
474 u8 seqnum, frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
475 u8 nom, lml; /* number of messages, last message len */
478 seqnum = 0x40 + flink.RequestSequenceNumber;
479 flink.RequestSequenceNumber =
480 (flink.RequestSequenceNumber + 1) & 0x07;
482 if (messagesize > FBUS_MAX_CONTENT_LENGTH) {
484 nom = (messagesize + FBUS_MAX_CONTENT_LENGTH - 1)
485 / FBUS_MAX_CONTENT_LENGTH;
486 lml = messagesize - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
488 for (i = 0; i < nom - 1; i++) {
490 memcpy(frame_buffer, message + (i * FBUS_MAX_CONTENT_LENGTH),
491 FBUS_MAX_CONTENT_LENGTH);
492 frame_buffer[FBUS_MAX_CONTENT_LENGTH] = nom - i;
493 frame_buffer[FBUS_MAX_CONTENT_LENGTH + 1] = seqnum;
495 FBUS_TX_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2,
496 messagetype, frame_buffer);
498 seqnum = flink.RequestSequenceNumber;
499 flink.RequestSequenceNumber = (flink.RequestSequenceNumber + 1) & 0x07;
502 memcpy(frame_buffer, message + ((nom - 1) * FBUS_MAX_CONTENT_LENGTH), lml);
503 frame_buffer[lml] = 0x01;
504 frame_buffer[lml + 1] = seqnum;
505 FBUS_TX_SendFrame(lml + 2, messagetype, frame_buffer);
509 memcpy(frame_buffer, message, messagesize);
510 frame_buffer[messagesize] = 0x01;
511 frame_buffer[messagesize + 1] = seqnum;
512 FBUS_TX_SendFrame(messagesize + 2, messagetype,
519 int FBUS_TX_SendAck(u8 message_type, u8 message_seq)
522 unsigned char request[2];
524 request[0] = message_type;
525 request[1] = message_seq;
527 dprintf("[Sending Ack of type %02x, seq: %x]\n",message_type, message_seq);
529 return FBUS_TX_SendFrame(2, 0x7f, request);
533 /* Initialise variables and start the link */
534 /* newlink is actually part of state - but the link code should not anything about state */
535 /* state is only passed around to allow for muliple state machines (one day...) */
537 GSM_Error FBUS_Initialise(GSM_Link *newlink, GSM_Statemachine *state, int type)
539 unsigned char init_char = 0x55;
542 if (type > 2) return GE_DEVICEOPENFAILED;
543 /* 'Copy in' the global structures */
545 statemachine = state;
547 /* Fill in the link functions */
548 glink->Loop = &FBUS_Loop;
549 glink->SendMessage = &FBUS_SendMessage;
551 /* Check for a valid init length */
552 if (glink->InitLength == 0)
553 glink->InitLength = 250;
555 /* Start up the link */
556 flink.RequestSequenceNumber = 0;
558 if (glink->ConnectionType == GCT_Infrared) {
560 return GE_DEVICEOPENFAILED;
561 } else { /* ConnectionType == GCT_Serial */
562 /* FBUS_OpenSerial(0) - try dau-9p
563 * FBUS_OpenSerial(n != 0) - try dlr-3p */
564 if (!FBUS_OpenSerial(type))
565 return GE_DEVICEOPENFAILED;
568 /* Send init string to phone, this is a bunch of 0x55 characters. Timing is
570 /* I believe that we need/can do this for any phone to get the UART synced */
572 for (count = 0; count < glink->InitLength; count++) {
574 device_write(&init_char, 1);
578 flink.i.state = FBUS_RX_Sync;
579 flink.i.BufferCount = 0;
580 for (count = 0; count < FBUS_MAX_MESSAGE_TYPES; count++) {
581 flink.messages[count].Malloced = 0;
582 flink.messages[count].FramesToGo = 0;
583 flink.messages[count].MessageLength = 0;