:pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
[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.1  2001/11/25 21:59:10  short
21   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
22
23   Revision 1.13  2001/11/17 20:14:15  pkot
24   Nasty bug with counting message length. Workaround applied. Needs fixing.
25
26   Revision 1.12  2001/11/15 12:04:05  pkot
27   Faster initialization for 6100 series (don't check for dlr3 cable)
28
29   Revision 1.11  2001/09/09 21:45:49  machek
30   Cleanups from Ladislav Michl <ladis@psi.cz>:
31
32   *) do *not* internationalize debug messages
33
34   *) some whitespace fixes, do not use //
35
36   *) break is unneccessary after return
37
38   Revision 1.10  2001/08/20 23:27:37  pkot
39   Add hardware shakehand to the link layer (Manfred Jonsson)
40
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.
44
45   Revision 1.8  2001/05/07 16:24:03  pkot
46   DLR-3P temporary fix. How should I do it better?
47
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.
50
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
54
55   Revision 1.5  2001/03/19 23:44:56  pkot
56   DLR3 cable support
57
58   Revision 1.4  2001/03/13 01:24:02  pkot
59   Code cleanup - no warnings during compilation
60
61   Revision 1.3  2001/03/13 01:23:18  pkot
62   Windows updates (Manfred Jonsson)
63
64   Revision 1.2  2001/03/11 11:18:39  machek
65   Made fbus link_dispatch (and fixed minor memory leak)
66
67   Revision 1.1  2001/02/21 19:57:06  chris
68   More fiddling with the directory layout
69
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
76
77   Revision 1.3  2001/02/06 21:15:34  chris
78   Preliminary irda support for 7110 etc.  Not well tested!
79
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)
83
84   Revision 1.1  2001/01/14 22:46:59  chris
85   Preliminary 7110 support (dlr9 only) and the beginnings of a new structure
86
87
88 */
89
90 /* System header files */
91
92 #include <stdio.h>
93 #include <string.h>
94 #include <stdlib.h>
95
96 /* Various header file */
97
98 #include "config.h"
99 #include "misc.h"
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"
105
106 #ifndef WIN32
107   #include "device.h"
108 #else
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))
114 #endif
115
116 #define __links_fbus_c
117 #include "links/fbus.h"
118
119 /* FIXME - pass device_* the link stuff?? */
120 /* FIXME - win32 stuff! */
121
122
123 /* Some globals */
124
125 static GSM_Link *glink;
126 static GSM_Statemachine *statemachine;
127 static FBUS_Link flink;         /* FBUS specific stuff, internal to this file */
128
129
130 /*--------------------------------------------*/
131
132 bool FBUS_OpenSerial(bool dlr3)
133 {
134         if (dlr3) dlr3 = 1;
135         /* Open device. */
136 #ifdef WIN32
137         if (OpenConnection(glink->PortDevice, FBUS_RX_StateMachine, NULL)) {
138 #else
139         if (!device_open(glink->PortDevice, false, false, false, GCT_Serial)) {
140 #endif
141                 perror(_("Couldn't open FBUS device"));
142                 return false;
143         }
144         device_changespeed(115200);
145
146         /* clearing the RTS bit and setting the DTR bit */
147         device_setdtrrts((1-dlr3), 0);
148
149         return (true);
150 }
151
152
153 /* RX_State machine for receive handling.  Called once for each character
154    received from the phone. */
155
156 void FBUS_RX_StateMachine(unsigned char rx_byte)
157 {
158
159         struct timeval time_diff;
160         FBUS_IncomingFrame *i = &flink.i;
161         int frm_num, seq_num;
162         FBUS_IncomingMessage *m;
163
164 #if 0
165         if (isprint(rx_byte))
166                 dprintf("[%02x%c]", (unsigned char) rx_byte, rx_byte);
167         else
168                 dprintf("[%02x ]", (unsigned char) rx_byte);
169 #endif
170
171         /* XOR the byte with the current checksum */
172         i->checksum[i->BufferCount & 1] ^= rx_byte;
173
174         switch (i->state) {
175
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
181                    sync byte */
182
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 */
188                         break;
189                 }
190
191                 /* else fall through to... */
192
193         case FBUS_RX_Sync:
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;
198                                 i->checksum[1] = 0;
199                                 i->state = FBUS_RX_GetDestination;
200                         } else {
201                                 /* Lost frame sync */
202                                 i->state = FBUS_RX_Discarding;
203                                 gettimeofday(&i->time_last, NULL);
204                         }
205
206                 } else {        /* glink->ConnectionType == GCT_Serial */
207                         if (rx_byte == FBUS_FRAME_ID) {
208                                 /* Initialize checksums. */
209                                 i->checksum[0] = FBUS_FRAME_ID;
210                                 i->checksum[1] = 0;
211                                 i->state = FBUS_RX_GetDestination;
212                         } else {
213                                 /* Lost frame sync */
214                                 i->state = FBUS_RX_Discarding;
215                                 gettimeofday(&i->time_last, NULL);
216                         }
217                 }
218
219                 break;
220
221         case FBUS_RX_GetDestination:
222
223                 i->MessageDestination = rx_byte;
224                 i->state = FBUS_RX_GetSource;
225
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 */
230
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);
234                 }
235
236                 break;
237
238         case FBUS_RX_GetSource:
239
240                 i->MessageSource = rx_byte;
241                 i->state = FBUS_RX_GetType;
242
243                 /* Source should be 0x00 */
244
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);
248                 }
249
250                 break;
251
252         case FBUS_RX_GetType:
253
254                 i->MessageType = rx_byte;
255                 i->state = FBUS_RX_GetLength1;
256
257                 break;
258
259         case FBUS_RX_GetLength1:
260
261                 i->FrameLength = rx_byte << 8;
262                 i->state = FBUS_RX_GetLength2;
263
264                 break;
265
266         case FBUS_RX_GetLength2:
267
268                 i->FrameLength = i->FrameLength + rx_byte;
269                 i->state = FBUS_RX_GetMessage;
270                 i->BufferCount = 0;
271
272                 break;
273
274         case FBUS_RX_GetMessage:
275
276                 i->MessageBuffer[i->BufferCount] = rx_byte;
277                 i->BufferCount++;
278                 
279                 if (i->BufferCount > FBUS_MAX_FRAME_LENGTH) {
280                         dprintf("FBUS: Message buffer overun - resetting\n");
281                         i->state = FBUS_RX_Sync;
282                         break;
283                 }
284
285                 /* If this is the last byte, it's the checksum. */
286
287                 if (i->BufferCount == i->FrameLength + (i->FrameLength % 2) + 2) {
288                         /* Is the checksum correct? */
289                         if (i->checksum[0] == i->checksum[1]) {
290
291                                 /* Deal with exceptions to the rules - acks and rlp.. */
292
293                                 if (i->MessageType == 0x7f) {
294                                         dprintf("[Received Ack of type %02x, seq: %2x]\n",
295                                                 i->MessageBuffer[0],(unsigned char) i->MessageBuffer[1]);
296
297                                 } else {        /* Normal message type */
298
299                                         /* Add data to the relevant Message buffer */
300                                         /* having checked the sequence number */
301
302                                         m = &flink.messages[i->MessageType];
303
304                                         frm_num = i->MessageBuffer[i->FrameLength - 2];
305                                         seq_num = i->MessageBuffer[i->FrameLength - 1];
306
307
308                                         /* 0x40 in the sequence number indicates first frame of a message */
309
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);
316                                                         m->Malloced = 0;
317                                                         m->MessageBuffer = NULL;
318                                                 }
319                                                 m->Malloced = frm_num * m->MessageLength;
320                                                 m->MessageBuffer = (char *) malloc(m->Malloced);
321
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 */
325                                         }
326
327                                         if (m->Malloced < m->MessageLength + i->FrameLength) {
328                                                 m->Malloced = m->MessageLength + i->FrameLength;
329                                                 m->MessageBuffer = (char *) realloc(m->MessageBuffer, m->Malloced);
330                                         }
331
332                                         memcpy(m->MessageBuffer + m->MessageLength, i->MessageBuffer,
333                                                i->FrameLength - 2);/* - (i->FrameLength % 2)); */
334
335                                         m->MessageLength += i->FrameLength - 2;/* - (i->FrameLength % 2); */
336
337                                         m->FramesToGo--;
338
339                                         /* Finally dispatch if ready */
340
341                                         if (m->FramesToGo == 0) {
342                                                 SM_IncomingFunction(statemachine, i->MessageType, m->MessageBuffer, m->MessageLength);
343                                                 free(m->MessageBuffer);
344                                                 m->MessageBuffer = NULL;
345                                                 m->Malloced = 0;
346                                         }
347
348                                         /* Send an ack (for all for now) */
349
350                                         FBUS_TX_SendAck(i->MessageType, seq_num & 0x0f);
351                                         
352                                 }
353                         } else {
354                                 dprintf("Bad checksum!\n");
355                         }
356                         i->state = FBUS_RX_Sync;
357                 }
358                 break;
359         }
360 }
361
362
363 /* This is the main loop function which must be called regularly */
364 /* timeout can be used to make it 'busy' or not */
365
366 GSM_Error FBUS_Loop(struct timeval *timeout)
367 {
368 #ifndef WIN32
369         unsigned char buffer[255];
370         int count, res;
371
372         res = device_select(timeout);
373         if (res > 0) {
374                 res = device_read(buffer, 255);
375                 for (count = 0; count < res; count++)
376                         FBUS_RX_StateMachine(buffer[count]);
377         } else
378                 return GE_TIMEOUT;
379
380         /* This traps errors from device_read */
381         if (res > 0)
382                 return GE_NONE;
383         else
384                 return GE_INTERNALERROR;
385 #else
386         return GE_NONE;
387 #endif
388 }
389
390
391
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... */
395
396 int FBUS_TX_SendFrame(u8 message_length, u8 message_type, u8 * buffer)
397 {
398
399         u8 out_buffer[FBUS_MAX_TRANSMIT_LENGTH + 5];
400         int count, current = 0;
401         unsigned char checksum;
402
403         /* FIXME - we should check for the message length ... */
404
405         /* Now construct the message header. */
406
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 */
411
412         out_buffer[current++] = FBUS_DEVICE_PHONE;      /* Destination */
413         out_buffer[current++] = FBUS_DEVICE_PC; /* Source */
414
415         out_buffer[current++] = message_type;   /* Type */
416
417         out_buffer[current++] = 0;      /* Length */
418
419         out_buffer[current++] = message_length; /* Length */
420
421         /* Copy in data if any. */
422
423         if (message_length != 0) {
424                 memcpy(out_buffer + current, buffer, message_length);
425                 current += message_length;
426         }
427
428         /* If the message length is odd we should add pad byte 0x00 */
429         if (message_length % 2)
430                 out_buffer[current++] = 0x00;
431
432         /* Now calculate checksums over entire message and append to message. */
433
434         /* Odd bytes */
435
436         checksum = 0;
437         for (count = 0; count < current; count += 2)
438                 checksum ^= out_buffer[count];
439
440         out_buffer[current++] = checksum;
441
442         /* Even bytes */
443
444         checksum = 0;
445         for (count = 1; count < current; count += 2)
446                 checksum ^= out_buffer[count];
447
448         out_buffer[current++] = checksum;
449
450 #ifdef DEBUG
451         fprintf(stderr, _("PC: "));
452
453         for (count = 0; count < current; count++)
454                 fprintf(stderr, "%02x:", out_buffer[count]);
455
456         fprintf(stderr, "\n");
457 #endif                          /* DEBUG */
458
459         /* Send it out... */
460
461         if (device_write(out_buffer, current) != current)
462                 return (false);
463
464         return (true);
465 }
466
467
468 /* Main function to send an fbus message */
469 /* Splits up the message into frames if necessary */
470
471 GSM_Error FBUS_SendMessage(u16 messagesize, u8 messagetype, void *message)
472 {
473
474         u8 seqnum, frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
475         u8 nom, lml;            /* number of messages, last message len */
476         int i;
477
478         seqnum = 0x40 + flink.RequestSequenceNumber;
479         flink.RequestSequenceNumber =
480             (flink.RequestSequenceNumber + 1) & 0x07;
481
482         if (messagesize > FBUS_MAX_CONTENT_LENGTH) {
483
484                 nom = (messagesize + FBUS_MAX_CONTENT_LENGTH - 1)
485                     / FBUS_MAX_CONTENT_LENGTH;
486                 lml = messagesize - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
487
488                 for (i = 0; i < nom - 1; i++) {
489
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;
494
495                         FBUS_TX_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2,
496                                           messagetype, frame_buffer);
497
498                         seqnum = flink.RequestSequenceNumber;
499                         flink.RequestSequenceNumber = (flink.RequestSequenceNumber + 1) & 0x07;
500                 }
501
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);
506
507         } else {
508
509                 memcpy(frame_buffer, message, messagesize);
510                 frame_buffer[messagesize] = 0x01;
511                 frame_buffer[messagesize + 1] = seqnum;
512                 FBUS_TX_SendFrame(messagesize + 2, messagetype,
513                                   frame_buffer);
514         }
515         return (GE_NONE);
516 }
517
518
519 int FBUS_TX_SendAck(u8 message_type, u8 message_seq)
520 {
521
522         unsigned char request[2];
523
524         request[0] = message_type;
525         request[1] = message_seq;
526
527         dprintf("[Sending Ack of type %02x, seq: %x]\n",message_type, message_seq);
528
529         return FBUS_TX_SendFrame(2, 0x7f, request);
530 }
531
532
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...) */
536
537 GSM_Error FBUS_Initialise(GSM_Link *newlink, GSM_Statemachine *state, int type)
538 {
539         unsigned char init_char = 0x55;
540         unsigned char count;
541
542         if (type > 2) return GE_DEVICEOPENFAILED;
543         /* 'Copy in' the global structures */
544         glink = newlink;
545         statemachine = state;
546
547         /* Fill in the link functions */
548         glink->Loop = &FBUS_Loop;
549         glink->SendMessage = &FBUS_SendMessage;
550
551         /* Check for a valid init length */
552         if (glink->InitLength == 0)
553                 glink->InitLength = 250;
554
555         /* Start up the link */
556         flink.RequestSequenceNumber = 0;
557
558         if (glink->ConnectionType == GCT_Infrared) {
559                 /* FIXME!! */
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;
566         }
567
568         /* Send init string to phone, this is a bunch of 0x55 characters. Timing is
569            empirical. */
570         /* I believe that we need/can do this for any phone to get the UART synced */
571
572         for (count = 0; count < glink->InitLength; count++) {
573                 usleep(100);
574                 device_write(&init_char, 1);
575         }
576
577         /* Init variables */
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;
584         }
585
586         return GE_NONE;
587 }