This commit was generated by cvs2svn to compensate for changes in r158,
[gnokii.git] / common / links / fbus.c
1 /*
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 2000 Hugh Blemings & Pavel Janík ml.
10   Copyright (C) 2000 Chris Kemp
11
12   Released under the terms of the GNU GPL, see file COPYING for more details.
13
14   This file provides an API for accessing functions via fbus. 
15   See README for more details on supported mobile phones.
16
17   The various routines are called FBUS_(whatever).
18
19   $Log$
20   Revision 1.1.1.3  2002/04/03 00:08:07  short
21   Found in "gnokii-working" directory, some November-patches version
22
23   Revision 1.11  2001/09/09 21:45:49  machek
24   Cleanups from Ladislav Michl <ladis@psi.cz>:
25
26   *) do *not* internationalize debug messages
27
28   *) some whitespace fixes, do not use //
29
30   *) break is unneccessary after return
31
32   Revision 1.10  2001/08/20 23:27:37  pkot
33   Add hardware shakehand to the link layer (Manfred Jonsson)
34
35   Revision 1.9  2001/05/28 09:25:16  pkot
36   Fixed autodetecting of the cable type in 6110 and 7110 series. DLR-3 is
37   tried first now. Seems to work ok with either 6130 or 6210.
38
39   Revision 1.8  2001/05/07 16:24:03  pkot
40   DLR-3P temporary fix. How should I do it better?
41
42   Revision 1.7  2001/03/22 16:17:05  chris
43   Tidy-ups and fixed gnokii/Makefile and gnokii/ChangeLog which I somehow corrupted.
44
45   Revision 1.6  2001/03/21 23:36:05  chris
46   Added the statemachine
47   This will break gnokii --identify and --monitor except for 6210/7110
48
49   Revision 1.5  2001/03/19 23:44:56  pkot
50   DLR3 cable support
51
52   Revision 1.4  2001/03/13 01:24:02  pkot
53   Code cleanup - no warnings during compilation
54
55   Revision 1.3  2001/03/13 01:23:18  pkot
56   Windows updates (Manfred Jonsson)
57
58   Revision 1.2  2001/03/11 11:18:39  machek
59   Made fbus link_dispatch (and fixed minor memory leak)
60
61   Revision 1.1  2001/02/21 19:57:06  chris
62   More fiddling with the directory layout
63
64   Revision 1.1  2001/02/16 14:29:52  chris
65   Restructure of common/.  Fixed a problem in fbus-phonet.c
66   Lots of dprintfs for Marcin
67   Any size xpm can now be loaded (eg for 7110 startup logos)
68   nk7110 code detects 7110/6210 and alters startup logo size to suit
69   Moved Marcin's extended phonebook code into gnokii.c
70
71   Revision 1.3  2001/02/06 21:15:34  chris
72   Preliminary irda support for 7110 etc.  Not well tested!
73
74   Revision 1.2  2001/02/03 23:56:14  chris
75   Start of work on irda support (now we just need fbus-irda.c!)
76   Proper unicode support in 7110 code (from pkot)
77
78   Revision 1.1  2001/01/14 22:46:59  chris
79   Preliminary 7110 support (dlr9 only) and the beginnings of a new structure
80
81
82 */
83
84 /* System header files */
85
86 #include <stdio.h>
87 #include <string.h>
88 #include <stdlib.h>
89
90 /* Various header file */
91
92 #include "config.h"
93 #include "misc.h"
94 #include "gsm-common.h"
95 #include "gsm-ringtones.h"
96 #include "gsm-networks.h"
97 #include "gsm-statemachine.h"
98 #include "links/utils.h"
99
100 #ifndef WIN32
101   #include "device.h"
102 #else
103   #include "win32/winserial.h"
104   #define device_write(a, b) WriteCommBlock(a, b)
105   #define device_read(a, b) ReadCommBlock(a, b)
106   #define sleep(x) Sleep((x) * 1000)
107   #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
108 #endif
109
110 #define __links_fbus_c
111 #include "links/fbus.h"
112
113 /* FIXME - pass device_* the link stuff?? */
114 /* FIXME - win32 stuff! */
115
116
117 /* Some globals */
118
119 static GSM_Link *glink;
120 static GSM_Statemachine *FBUS_statemachine;
121 static FBUS_Link flink;         /* FBUS specific stuff, internal to this file */
122
123
124 /*--------------------------------------------*/
125
126 bool FBUS_OpenSerial(bool dlr3)
127 {
128         if (dlr3) dlr3 = 1;
129         /* Open device. */
130 #ifdef WIN32
131         if (OpenConnection(glink->PortDevice, FBUS_RX_StateMachine, NULL)) {
132 #else
133         if (!device_open(glink->PortDevice, false, false, false, GCT_Serial)) {
134 #endif
135                 perror(_("Couldn't open FBUS device"));
136                 return false;
137         }
138         device_changespeed(115200);
139
140         /* clearing the RTS bit and setting the DTR bit */
141         device_setdtrrts((1-dlr3), 0);
142
143         return (true);
144 }
145
146
147 /* RX_State machine for receive handling.  Called once for each character
148    received from the phone. */
149
150 void FBUS_RX_StateMachine(unsigned char rx_byte)
151 {
152
153         struct timeval time_diff;
154         FBUS_IncomingFrame *i = &flink.i;
155         int frm_num, seq_num;
156         FBUS_IncomingMessage *m;
157
158 #if 0
159         if (isprint(rx_byte))
160                 fprintf(stderr, "[%02x%c]", (unsigned char) rx_byte, rx_byte);
161         else
162                 fprintf(stderr, "[%02x ]", (unsigned char) rx_byte);
163 #endif
164
165         /* XOR the byte with the current checksum */
166         i->checksum[i->BufferCount & 1] ^= rx_byte;
167
168         switch (i->state) {
169
170                 /* Messages from the phone start with an 0x1e (cable) or 0x1c (IR).
171                    We use this to "synchronise" with the incoming data stream. However,
172                    if we see something else, we assume we have lost sync and we require
173                    a gap of at least 5ms before we start looking again. This is because
174                    the data part of the frame could contain a byte which looks like the
175                    sync byte */
176
177         case FBUS_RX_Discarding:
178                 gettimeofday(&i->time_now, NULL);
179                 timersub(&i->time_now, &i->time_last, &time_diff);
180                 if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
181                         i->time_last = i->time_now;     /* no gap seen, continue discarding */
182                         break;
183                 }
184
185                 /* else fall through to... */
186
187         case FBUS_RX_Sync:
188                 if (glink->ConnectionType == GCT_Infrared) {
189                         if (rx_byte == FBUS_IR_FRAME_ID) {
190                                 /* Initialize checksums. */
191                                 i->checksum[0] = FBUS_IR_FRAME_ID;
192                                 i->checksum[1] = 0;
193                                 i->state = FBUS_RX_GetDestination;
194                         } else {
195                                 /* Lost frame sync */
196                                 i->state = FBUS_RX_Discarding;
197                                 gettimeofday(&i->time_last, NULL);
198                         }
199
200                 } else {        /* glink->ConnectionType == GCT_Serial */
201                         if (rx_byte == FBUS_FRAME_ID) {
202                                 /* Initialize checksums. */
203                                 i->checksum[0] = FBUS_FRAME_ID;
204                                 i->checksum[1] = 0;
205                                 i->state = FBUS_RX_GetDestination;
206                         } else {
207                                 /* Lost frame sync */
208                                 i->state = FBUS_RX_Discarding;
209                                 gettimeofday(&i->time_last, NULL);
210                         }
211                 }
212
213                 break;
214
215         case FBUS_RX_GetDestination:
216
217                 i->MessageDestination = rx_byte;
218                 i->state = FBUS_RX_GetSource;
219
220                 /* When there is a checksum error and things get out of sync we have to manage to resync */
221                 /* If doing a data call at the time, finding a 0x1e etc is really quite likely in the data stream */
222                 /* Then all sorts of horrible things happen because the packet length etc is wrong... */
223                 /* Therefore we test here for a destination of 0x0c and return to the top if it is not */
224
225                 if (rx_byte != 0x0c) {
226                         i->state = FBUS_RX_Sync;
227                         dprintf("The fbus stream is out of sync - expected 0x0c, got %2x\n", rx_byte);
228                 }
229
230                 break;
231
232         case FBUS_RX_GetSource:
233
234                 i->MessageSource = rx_byte;
235                 i->state = FBUS_RX_GetType;
236
237                 /* Source should be 0x00 */
238
239                 if (rx_byte != 0x00) {
240                         i->state = FBUS_RX_Sync;
241                         dprintf("The fbus stream is out of sync - expected 0x00, got %2x\n",rx_byte);
242                 }
243
244                 break;
245
246         case FBUS_RX_GetType:
247
248                 i->MessageType = rx_byte;
249                 i->state = FBUS_RX_GetLength1;
250
251                 break;
252
253         case FBUS_RX_GetLength1:
254
255                 i->FrameLength = rx_byte << 8;
256                 i->state = FBUS_RX_GetLength2;
257
258                 break;
259
260         case FBUS_RX_GetLength2:
261
262                 i->FrameLength = i->FrameLength + rx_byte;
263                 i->state = FBUS_RX_GetMessage;
264                 i->BufferCount = 0;
265
266                 break;
267
268         case FBUS_RX_GetMessage:
269
270                 i->MessageBuffer[i->BufferCount] = rx_byte;
271                 i->BufferCount++;
272                 
273                 if (i->BufferCount > FBUS_MAX_FRAME_LENGTH) {
274                         dprintf("FBUS: Message buffer overun - resetting\n");
275                         i->state = FBUS_RX_Sync;
276                         break;
277                 }
278
279                 /* If this is the last byte, it's the checksum. */
280
281                 if (i->BufferCount == i->FrameLength + (i->FrameLength % 2) + 2) {
282                         /* Is the checksum correct? */
283                         if (i->checksum[0] == i->checksum[1]) {
284
285                                 /* Deal with exceptions to the rules - acks and rlp.. */
286
287                                 if (i->MessageType == 0x7f) {
288                                         dprintf("[Received Ack of type %02x, seq: %2x]\n",
289                                                 i->MessageBuffer[0],(unsigned char) i->MessageBuffer[1]);
290
291                                 } else {        /* Normal message type */
292
293                                         /* Add data to the relevant Message buffer */
294                                         /* having checked the sequence number */
295
296                                         m = &flink.messages[i->MessageType];
297
298                                         frm_num = i->MessageBuffer[i->FrameLength - 2];
299                                         seq_num = i->MessageBuffer[i->FrameLength - 1];
300
301
302                                         /* 0x40 in the sequence number indicates first frame of a message */
303
304                                         if ((seq_num & 0x40) == 0x40) {
305                                                 /* Fiddle around and malloc some memory */
306                                                 m->MessageLength = 0;
307                                                 m->FramesToGo = frm_num;
308                                                 if (m->Malloced != 0) {
309                                                         free(m->MessageBuffer);
310                                                         m->Malloced = 0;
311                                                         m->MessageBuffer = NULL;
312                                                 }
313                                                 m->Malloced = frm_num *m->MessageLength;
314                                                 m->MessageBuffer = (char *) malloc(m->Malloced);
315
316                                         } else if (m->FramesToGo != frm_num) {
317                                                 fprintf(stdout, "Missed a frame in a multiframe message.\n");
318                                                 /* FIXME - we should make sure we don't ack the rest etc */
319                                         }
320
321                                         if (m->Malloced < m->MessageLength + i->FrameLength) {
322                                                 m->Malloced = m->MessageLength + i->FrameLength;
323                                                 m->MessageBuffer = (char *) realloc(m->MessageBuffer, m->Malloced);
324                                         }
325
326                                         memcpy(m->MessageBuffer + m->MessageLength, i->MessageBuffer,
327                                                i->FrameLength - 2 - (i->FrameLength % 2));
328
329                                         m->MessageLength += i->FrameLength - 2 - (i->FrameLength % 2);
330
331                                         m->FramesToGo--;
332
333                                         /* Finally dispatch if ready */
334
335                                         if (m->FramesToGo == 0) {
336                                                 SM_IncomingFunction(FBUS_statemachine, i->MessageType, m->MessageBuffer, m->MessageLength);
337                                                 free(m->MessageBuffer);
338                                                 m->MessageBuffer = NULL;
339                                                 m->Malloced = 0;
340                                         }
341
342                                         /* Send an ack (for all for now) */
343
344                                         FBUS_TX_SendAck(i->MessageType, seq_num & 0x0f);
345                                         
346                                 }
347                         } else {
348                                 dprintf("Bad checksum!\n");
349                         }
350                         i->state = FBUS_RX_Sync;
351                 }
352                 break;
353         }
354 }
355
356
357 /* This is the main loop function which must be called regularly */
358 /* timeout can be used to make it 'busy' or not */
359
360 GSM_Error FBUS_Loop(struct timeval *timeout)
361 {
362 #ifndef WIN32
363         unsigned char buffer[255];
364         int count, res;
365
366         res = device_select(timeout);
367         if (res > 0) {
368                 res = device_read(buffer, 255);
369                 for (count = 0; count < res; count++)
370                         FBUS_RX_StateMachine(buffer[count]);
371         } else
372                 return GE_TIMEOUT;
373
374         /* This traps errors from device_read */
375         if (res > 0)
376                 return GE_NONE;
377         else
378                 return GE_INTERNALERROR;
379 #else
380         return GE_NONE;
381 #endif
382 }
383
384
385
386 /* Prepares the message header and sends it, prepends the message start byte
387            (0x1e) and other values according the value specified when called.
388            Calculates checksum and then sends the lot down the pipe... */
389
390 int FBUS_TX_SendFrame(u8 message_length, u8 message_type, u8 * buffer)
391 {
392
393         u8 out_buffer[FBUS_MAX_TRANSMIT_LENGTH + 5];
394         int count, current = 0;
395         unsigned char checksum;
396
397         /* FIXME - we should check for the message length ... */
398
399         /* Now construct the message header. */
400
401         if (glink->ConnectionType == GCT_Infrared)
402                 out_buffer[current++] = FBUS_IR_FRAME_ID;       /* Start of the IR frame indicator */
403         else                    /* ConnectionType == GCT_Serial */
404                 out_buffer[current++] = FBUS_FRAME_ID;  /* Start of the frame indicator */
405
406         out_buffer[current++] = FBUS_DEVICE_PHONE;      /* Destination */
407         out_buffer[current++] = FBUS_DEVICE_PC; /* Source */
408
409         out_buffer[current++] = message_type;   /* Type */
410
411         out_buffer[current++] = 0;      /* Length */
412
413         out_buffer[current++] = message_length; /* Length */
414
415         /* Copy in data if any. */
416
417         if (message_length != 0) {
418                 memcpy(out_buffer + current, buffer, message_length);
419                 current += message_length;
420         }
421
422         /* If the message length is odd we should add pad byte 0x00 */
423         if (message_length % 2)
424                 out_buffer[current++] = 0x00;
425
426         /* Now calculate checksums over entire message and append to message. */
427
428         /* Odd bytes */
429
430         checksum = 0;
431         for (count = 0; count < current; count += 2)
432                 checksum ^= out_buffer[count];
433
434         out_buffer[current++] = checksum;
435
436         /* Even bytes */
437
438         checksum = 0;
439         for (count = 1; count < current; count += 2)
440                 checksum ^= out_buffer[count];
441
442         out_buffer[current++] = checksum;
443
444 #ifdef DEBUG
445         fprintf(stderr, _("PC: "));
446
447         for (count = 0; count < current; count++)
448                 fprintf(stderr, "%02x:", out_buffer[count]);
449
450         fprintf(stderr, "\n");
451 #endif                          /* DEBUG */
452
453         /* Send it out... */
454
455         if (device_write(out_buffer, current) != current)
456                 return (false);
457
458         return (true);
459 }
460
461
462 /* Main function to send an fbus message */
463 /* Splits up the message into frames if necessary */
464
465 GSM_Error FBUS_SendMessage(u16 messagesize, u8 messagetype, void *message)
466 {
467
468         u8 seqnum, frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
469         u8 nom, lml;            /* number of messages, last message len */
470         int i;
471
472         seqnum = 0x40 + flink.RequestSequenceNumber;
473         flink.RequestSequenceNumber =
474             (flink.RequestSequenceNumber + 1) & 0x07;
475
476         if (messagesize > FBUS_MAX_CONTENT_LENGTH) {
477
478                 nom = (messagesize + FBUS_MAX_CONTENT_LENGTH - 1)
479                     / FBUS_MAX_CONTENT_LENGTH;
480                 lml = messagesize - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
481
482                 for (i = 0; i < nom - 1; i++) {
483
484                         memcpy(frame_buffer, message + (i * FBUS_MAX_CONTENT_LENGTH),
485                                FBUS_MAX_CONTENT_LENGTH);
486                         frame_buffer[FBUS_MAX_CONTENT_LENGTH] = nom - i;
487                         frame_buffer[FBUS_MAX_CONTENT_LENGTH + 1] = seqnum;
488
489                         FBUS_TX_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2,
490                                           messagetype, frame_buffer);
491
492                         seqnum = flink.RequestSequenceNumber;
493                         flink.RequestSequenceNumber = (flink.RequestSequenceNumber + 1) & 0x07;
494                 }
495
496                 memcpy(frame_buffer, message + ((nom - 1) * FBUS_MAX_CONTENT_LENGTH), lml);
497                 frame_buffer[lml] = 0x01;
498                 frame_buffer[lml + 1] = seqnum;
499                 FBUS_TX_SendFrame(lml + 2, messagetype, frame_buffer);
500
501         } else {
502
503                 memcpy(frame_buffer, message, messagesize);
504                 frame_buffer[messagesize] = 0x01;
505                 frame_buffer[messagesize + 1] = seqnum;
506                 FBUS_TX_SendFrame(messagesize + 2, messagetype,
507                                   frame_buffer);
508         }
509         return (GE_NONE);
510 }
511
512
513 int FBUS_TX_SendAck(u8 message_type, u8 message_seq)
514 {
515
516         unsigned char request[2];
517
518         request[0] = message_type;
519         request[1] = message_seq;
520
521         dprintf("[Sending Ack of type %02x, seq: %x]\n",message_type, message_seq);
522
523         return FBUS_TX_SendFrame(2, 0x7f, request);
524 }
525
526
527 /* Initialise variables and start the link */
528 /* newlink is actually part of state - but the link code should not anything about state */
529 /* state is only passed around to allow for muliple state machines (one day...) */
530
531 GSM_Error FBUS_Initialise(GSM_Link *newlink, GSM_Statemachine *state)
532 {
533         unsigned char init_char = 0x55;
534         unsigned char count;
535         static int try = 0;
536
537         try++;
538         if (try > 2) return GE_DEVICEOPENFAILED;
539         /* 'Copy in' the global structures */
540         glink = newlink;
541         FBUS_statemachine = state;
542
543         /* Fill in the link functions */
544         glink->Loop = &FBUS_Loop;
545         glink->SendMessage = &FBUS_SendMessage;
546
547         /* Check for a valid init length */
548         if (glink->InitLength == 0)
549                 glink->InitLength = 250;
550
551         /* Start up the link */
552         flink.RequestSequenceNumber = 0;
553
554         if (glink->ConnectionType == GCT_Infrared) {
555                 /* FIXME!! */
556                 return GE_DEVICEOPENFAILED;
557         } else {                /* ConnectionType == GCT_Serial */
558                 /* FBUS_OpenSerial(0) - try dau-9p
559                  * FBUS_OpenSerial(n != 0) - try dlr-3p */
560                 if (!FBUS_OpenSerial(2 - try))
561                         return GE_DEVICEOPENFAILED;
562         }
563
564         /* Send init string to phone, this is a bunch of 0x55 characters. Timing is
565            empirical. */
566         /* I believe that we need/can do this for any phone to get the UART synced */
567
568         for (count = 0; count < glink->InitLength; count++) {
569                 usleep(100);
570                 device_write(&init_char, 1);
571         }
572
573         /* Init variables */
574         flink.i.state = FBUS_RX_Sync;
575         flink.i.BufferCount = 0;
576         for (count = 0; count < FBUS_MAX_MESSAGE_TYPES; count++) {
577                 flink.messages[count].Malloced = 0;
578                 flink.messages[count].FramesToGo = 0;
579                 flink.messages[count].MessageLength = 0;
580         }
581
582         return GE_NONE;
583 }