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