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