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