This commit was manufactured by cvs2svn to create tag 'bp_uc'.
[gnokii.git] / common / protocol / fbus.c
1 /*
2
3   G N O K I I
4
5   A Linux/Unix toolset and driver for Nokia mobile phones.
6
7   Released under the terms of the GNU GPL, see file COPYING for more details.
8
9   This file provides an API for support for FBUS protocol
10
11 */
12
13 /* "Turn on" prototypes in fbus.h */
14 #define __fbus_c 
15
16 /* System header files */
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20  
21 #ifdef WIN32
22   #include <windows.h>
23   #include "misc_win32.h"
24 #else
25   #include <ctype.h>
26 #endif
27
28 /* Various header file */
29 #include "devices/device.h"
30 #include "gsm-api.h"
31 #include "protocol/fbus.h"
32 #include "protocol/at.h"
33 #include "newmodules/newat.h"
34 #include "newmodules/n6110.h"
35 #include "misc.h"
36
37 GSM_Protocol FBUS_Functions = {
38   FBUS_Initialise,
39   FBUS_SendMessage,
40   FBUS_SendFrame,
41   NULL_WritePhone,
42   FBUS_Terminate,
43   FBUS_RX_StateMachine
44 };
45
46 /* Local variables */
47 enum FBUS_RX_States RX_State;
48
49 u8 MessageDestination, MessageSource;
50
51 u16 BufferCount, MultiBufferCount;
52
53 u16 MessageLength, MultiMessageLength;
54
55 bool RX_Multiple = false;
56
57 u8 MessageType,MultiMessageType;
58
59 u8 MessageBuffer[FBUS_MAX_RECEIVE_LENGTH * 6],MultiMessageBuffer[FBUS_MAX_RECEIVE_LENGTH * 6];
60
61 u8        RequestSequenceNumber = 0x00;
62
63 #ifdef DEBUG    
64 char *N61_PrintDevice(int Device)
65 {
66   switch (Device) {
67
68   case FBUS_DEVICE_PHONE:return _("Phone");
69   case FBUS_DEVICE_PC   :return _("PC");
70   default               :return _("Unknown");
71   }
72 }
73 #endif /* DEBUG */
74
75 /* N61_RX_DisplayMessage is called when a message we don't know about is
76    received so that the user can see what is going back and forth, and perhaps
77    shed some more light/explain another message type! */
78 void N61_RX_DisplayMessage()
79 {
80 #ifdef DEBUG
81   fprintf(stdout, _("Msg Dest: %s\n"), N61_PrintDevice(MessageDestination));
82   fprintf(stdout, _("Msg Source: %s\n"), N61_PrintDevice(MessageSource));
83   fprintf(stdout, _("Msg Type: %02x\n"), MessageType);
84
85   hexdump(MessageLength-2,MessageBuffer);
86 #endif
87
88   AppendLog(MessageBuffer,MessageLength-2,true);
89 }
90
91 /* Prepares the message header and sends it, prepends the message start byte
92            (0x1e) and other values according the value specified when called.
93            Calculates checksum and then sends the lot down the pipe... */
94 int FBUS_SendFrame(u16 message_length, u8 message_type, u8 *buffer) {
95
96   /* Originally out_buffer[FBUS_MAX_CONTENT_LENGTH + 2],
97      but it made problems with MBUS */
98   u8 out_buffer[1000];
99   
100   int count, current=0;
101   unsigned char checksum;
102
103   /* FIXME - we should check for the message length ... */
104
105   /* Now construct the message header. */
106
107   if (CurrentConnectionType==GCT_FBUS)
108     out_buffer[current++] = FBUS_FRAME_ID;    /* Start of the frame indicator */
109   else
110     out_buffer[current++] = FBUS_IR_FRAME_ID; /* Start of the frame indicator */
111     
112   out_buffer[current++] = FBUS_DEVICE_PHONE; /* Destination */
113
114   out_buffer[current++] = FBUS_DEVICE_PC;    /* Source */
115
116   out_buffer[current++] = message_type; /* Type */
117
118   out_buffer[current++] = 0; /* Length1 */
119   out_buffer[current++] = message_length; /* Length2 */
120
121   /* Copy in data if any. */            
122   if (message_length != 0) {
123     memcpy(out_buffer + current, buffer, message_length);
124     current+=message_length;
125   }
126
127   /* If the message length is odd we should add pad byte 0x00 */
128   if (message_length % 2)
129     out_buffer[current++]=0x00;
130
131   /* Now calculate checksums over entire message and append to message. */
132
133   /* Odd bytes */
134   checksum = 0;
135   for (count = 0; count < current; count+=2)
136     checksum ^= out_buffer[count];
137
138   out_buffer[current++] = checksum;
139
140   /* Even bytes */
141   checksum = 0;
142   for (count = 1; count < current; count+=2)
143     checksum ^= out_buffer[count];
144
145   out_buffer[current++] = checksum;
146   
147 #ifdef DEBUG
148   NULL_TX_DisplayMessage(current, out_buffer);
149 #endif /* DEBUG */
150
151   /* Send it out... */
152   if (!NULL_WritePhone(current,out_buffer))
153     return (false);
154
155   return (true);
156 }
157
158 int FBUS_SendMessage(u16 message_length, u8 message_type, u8 *buffer) {
159   
160   u8 seqnum;
161
162   u8 frame_buffer[FBUS_MAX_CONTENT_LENGTH + 2];
163
164   u8 nom, lml;  /* number of messages, last message len */
165   int i;
166
167   seqnum = 0x40 + RequestSequenceNumber;
168   RequestSequenceNumber = (RequestSequenceNumber + 1) & 0x07;
169  
170   if (message_length > FBUS_MAX_CONTENT_LENGTH) {
171
172     nom = (message_length + FBUS_MAX_CONTENT_LENGTH - 1)
173                           / FBUS_MAX_CONTENT_LENGTH;
174     lml = message_length - ((nom - 1) * FBUS_MAX_CONTENT_LENGTH);
175
176     for (i = 0; i < nom - 1; i++) {
177
178       memcpy(frame_buffer, buffer + (i * FBUS_MAX_CONTENT_LENGTH),
179              FBUS_MAX_CONTENT_LENGTH);
180       frame_buffer[FBUS_MAX_CONTENT_LENGTH] = nom - i;
181       frame_buffer[FBUS_MAX_CONTENT_LENGTH + 1] = seqnum;
182
183       FBUS_SendFrame(FBUS_MAX_CONTENT_LENGTH + 2, message_type,
184                      frame_buffer);
185
186       seqnum = RequestSequenceNumber;
187       RequestSequenceNumber = (RequestSequenceNumber + 1) & 0x07;    
188     }
189
190     memcpy(frame_buffer, buffer + ((nom - 1) * FBUS_MAX_CONTENT_LENGTH), lml);
191     frame_buffer[lml] = 0x01;
192     frame_buffer[lml + 1] = seqnum;
193     FBUS_SendFrame(lml + 2, message_type, frame_buffer);
194
195   } else {
196
197     memcpy(frame_buffer, buffer, message_length);
198     frame_buffer[message_length] = 0x01;
199     frame_buffer[message_length + 1] = seqnum;
200     FBUS_SendFrame(message_length + 2, message_type, frame_buffer);
201   }
202
203   return (true);
204 }
205
206 int FBUS_SendAck(u8 message_type, u8 message_seq) {
207
208   unsigned char request[6];
209
210   request[0] = message_type;
211   request[1] = message_seq;
212
213 #ifdef DEBUG
214   fprintf(stdout, _("[Sending Ack of type %02x, seq: %x]\n"), message_type, message_seq);
215 #endif /* DEBUG */
216
217   return FBUS_SendFrame(2, FBUS_FRTYPE_ACK, request);
218 }
219
220 /* Applications should call FBUS_Terminate to shut down the FBUS thread and
221    close the serial port. */
222 void FBUS_Terminate(void)
223 {
224   /* Request termination of thread */
225   CurrentRequestTerminate = true;
226
227   /* Close serial port. */
228   device_close();
229 }
230
231 /* RX_State machine for receive handling.  Called once for each character
232    received from the phone/phone. */
233
234 void FBUS_RX_StateMachine(unsigned char rx_byte) {
235
236   static struct timeval time_now, time_last, time_diff;
237   
238   static int checksum[2];
239   
240   int i=0;
241
242 #ifdef DEBUG
243   /* For model sniff only display received bytes */
244   if (strcmp(GSM_Info->FBUSModels, "sniff"))
245   {
246 #endif
247
248   /* XOR the byte with the current checksum */
249   checksum[BufferCount&1] ^= rx_byte;
250
251   switch (RX_State) {
252         
253     /* Messages from the phone start with an 0x1e (FBUS) or 0x1c (IR) or 0x1f (MBUS).
254        We use this to "synchronise" with the incoming data stream. However,
255        if we see something else, we assume we have lost sync and we require
256        a gap of at least 5ms before we start looking again. This is because
257        the data part of the frame could contain a byte which looks like the
258        sync byte */
259
260   case FBUS_RX_Discarding:
261
262 #ifndef VC6
263     gettimeofday(&time_now, NULL);
264     timersub(&time_now, &time_last, &time_diff);
265     if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
266       time_last = time_now;  /* no gap seen, continue discarding */
267       break;
268     }
269     /* else fall through to... */
270 #endif
271
272   case FBUS_RX_Sync:
273
274       if ((CurrentConnectionType==GCT_FBUS && rx_byte == FBUS_FRAME_ID) ||
275           ((CurrentConnectionType==GCT_Infrared ||
276            CurrentConnectionType==GCT_Tekram) && rx_byte == FBUS_IR_FRAME_ID)) {
277
278         BufferCount = 0;
279
280         RX_State = FBUS_RX_GetDestination;
281         
282         /* Initialize checksums. */
283         checksum[0] = rx_byte;
284         checksum[1] = 0;
285       } else {
286         /* Lost frame sync */
287         RX_State = FBUS_RX_Discarding;
288 #ifndef VC6
289         gettimeofday(&time_last, NULL);
290 #endif
291       }    
292     break;
293
294   case FBUS_RX_GetDestination:
295
296     MessageDestination=rx_byte;
297     RX_State = FBUS_RX_GetSource;
298
299     /* When there is a checksum error and things get out of sync we have to manage to resync */
300     /* If doing a data call at the time, finding a 0x1e etc is really quite likely in the data stream */
301     /* Then all sorts of horrible things happen because the packet length etc is wrong... */
302     /* Therefore we test here for a destination of 0x0c and return to the top if it is not */
303     if (rx_byte!=FBUS_DEVICE_PC && strstr(GSM_Info->FBUSModels, "sniff")==NULL) {
304       RX_State=FBUS_RX_Sync;
305 #ifdef DEBUG
306       fprintf(stdout,"The fbus stream is out of sync - expected 0x0c, got %2x\n",rx_byte);
307 #endif
308       AppendLogText("SYNC\n",false);
309     }
310
311     break;
312
313   case FBUS_RX_GetSource:
314
315     MessageSource=rx_byte;
316     RX_State = FBUS_RX_GetType;
317
318     /* Source should be 0x00 */
319     if (rx_byte!=FBUS_DEVICE_PHONE && strstr(GSM_Info->FBUSModels, "sniff")==NULL)  {
320       RX_State=FBUS_RX_Sync;
321 #ifdef DEBUG
322       fprintf(stdout,"The fbus stream is out of sync - expected 0x00, got %2x\n",rx_byte);
323 #endif
324       AppendLogText("SYNC\n",false);
325     }
326     
327     break;
328
329   case FBUS_RX_GetType:
330
331     MessageType=rx_byte;
332
333     RX_State = FBUS_RX_GetLength1;
334
335     break;
336
337   case FBUS_RX_GetLength1:
338
339     MessageLength = 0;
340
341     RX_State = FBUS_RX_GetLength2;
342     
343     break;
344     
345   case FBUS_RX_GetLength2:
346
347     /* MW:Here are problems with conversion. For chars 0-127 it's OK, for
348        higher not (probably because rx_byte is char type) - improtant
349        for MBUS. So, I make it double and strange - generally it should be
350        more simple and make simple convert rx_byte into MessageLength */      
351 #if defined(__svr4__) || defined(__FreeBSD__)
352     if (rx_byte!=0) {
353       for (i=0;i<rx_byte;i++)
354         MessageLength=MessageLength++;
355     }
356 #else
357     MessageLength = rx_byte;
358 #endif
359     
360     RX_State = FBUS_RX_GetMessage;
361     
362     break;
363     
364   case FBUS_RX_GetMessage:
365
366     MessageBuffer[BufferCount] = rx_byte;
367     BufferCount ++;
368     
369     if (BufferCount>FBUS_MAX_RECEIVE_LENGTH*6) {
370 #ifdef DEBUG
371       fprintf(stdout, "FB61: Message buffer overun - resetting\n");
372 #endif
373       AppendLogText("OVERUN\n",false);
374       RX_Multiple=false;
375       RX_State = FBUS_RX_Sync;
376       break;
377     }
378
379     /* If this is the last byte, it's the checksum. */
380     if (BufferCount == MessageLength+(MessageLength%2)+2) {
381
382       /* Is the checksum correct? */
383       if (checksum[0] == checksum[1]) {
384           
385         if (RX_Multiple) {
386
387           if (MessageType==MultiMessageType) {
388
389             if (MessageLength+MultiMessageLength>FBUS_MAX_RECEIVE_LENGTH*6) {
390 #ifdef DEBUG
391               fprintf(stdout, "FB61: Message buffer overun - resetting\n");
392 #endif
393               AppendLogText("OVERUN\n",false);
394               RX_Multiple=false;
395               RX_State = FBUS_RX_Sync;
396               break;
397             }
398         
399             /* We copy next part of multiframe message into special buffer */
400             for (i=0;i<MessageLength;i++) {
401               MultiMessageBuffer[i+MultiMessageLength]=MessageBuffer[i];
402             }
403             MultiMessageLength=MultiMessageLength+MessageLength-2;
404
405             FBUS_SendAck(MessageType, MessageBuffer[MessageLength-1] & 0x0f);           
406                 
407             if ((MessageLength > 1) && (MessageBuffer[MessageLength-2] != 0x01))
408             {
409             } else {
410               for (i=0;i<MultiMessageLength+2;i++) {
411                 MessageBuffer[i]=MultiMessageBuffer[i];
412               }
413               MessageLength=MultiMessageLength+2;
414               RX_Multiple=false;
415
416               /* Do not debug Ack and RLP frames to detail. */
417               if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1)
418                 N61_RX_DisplayMessage();
419
420               GSM->DispatchMessage(MessageLength-2, MessageBuffer, MessageType);
421             }
422
423           } else {
424
425             /* We do not want to send ACK of ACKs and ACK of RLP frames. */
426             if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1) {
427               FBUS_SendAck(MessageType, MessageBuffer[MessageLength-1] & 0x0f);
428
429               if ((MessageLength > 1) && (MessageBuffer[MessageLength-2] != 0x01))
430               {
431 #ifdef DEBUG
432                 fprintf(stdout,_("Multiframe message in multiframe message !\n"));
433                 fprintf(stdout,_("Please report it !\n"));
434 #endif
435                 RX_State = FBUS_RX_Sync;
436               }
437             }
438           }
439         } else {
440
441           /* We do not want to send ACK of ACKs and ACK of RLP frames. */
442           if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1) {
443             FBUS_SendAck(MessageType, MessageBuffer[MessageLength-1] & 0x0f);
444
445             if ((MessageLength > 1) && (MessageBuffer[MessageLength-2] != 0x01))
446             {
447               /* We copy previous part of multiframe message into special buffer */
448               RX_Multiple = true;
449               for (i=0;i<MessageLength-2;i++) {
450                 MultiMessageBuffer[i]=MessageBuffer[i];
451               }
452               MultiMessageLength=MessageLength-2;
453               MultiMessageType=MessageType;
454             }
455           }
456
457           if (!RX_Multiple && MessageDestination!=FBUS_DEVICE_PHONE ) {
458             /* Do not debug Ack and RLP frames to detail. */
459             if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1)
460               N61_RX_DisplayMessage();
461
462             GSM->DispatchMessage(MessageLength-2, MessageBuffer, MessageType);
463           }
464
465 #ifdef DEBUG
466           /* When make debug and message is to phone display it */
467           if (MessageDestination==FBUS_DEVICE_PHONE) {
468             for (i=MessageLength;i>=0;i--)
469               MessageBuffer[i+6]=MessageBuffer[i];
470             MessageBuffer[0]=FBUS_FRAME_ID;
471             MessageBuffer[1]=FBUS_DEVICE_PHONE;
472             MessageBuffer[2]=FBUS_DEVICE_PC;
473             MessageBuffer[3]=MessageType;
474             MessageBuffer[4]=0;
475             MessageBuffer[5]=MessageLength;
476             MessageLength=MessageLength+8;
477             if (MessageLength % 2) MessageLength++;
478             NULL_TX_DisplayMessage(MessageLength, MessageBuffer);
479           }
480 #endif    
481         }
482       } else {
483 #ifdef DEBUG
484           fprintf(stdout, _("Bad checksum %02x (should be %02x), msg len=%i !\n"),checksum[0],checksum[1],MessageLength);
485 #endif /* DEBUG */
486         AppendLogText("CHECKSUM\n",false);
487
488         /* Just to be sure! */
489         RX_Multiple=false;
490       }
491       RX_State = FBUS_RX_Sync;    
492     }
493     break;
494   }
495
496 #ifdef DEBUG
497   } else {
498     if (isprint(rx_byte))
499       fprintf(stdout, "[%02x%c]", rx_byte, rx_byte);
500     else
501       fprintf(stdout, "[%02x ]", rx_byte);
502
503   }
504 #endif
505 }
506   
507 /* Called by initialisation code to open comm port in asynchronous mode. */
508 bool FBUS_OpenSerial(void)
509 {
510   /* Uncomment, if want to test first method for DLR3 */
511   unsigned char req[3]   = {"AT\r"};  
512   unsigned char req2[5]  = {"AT&F\r"};  
513   unsigned char req3[13] = {"AT*NOKIAFBUS\r"};  
514
515   switch (CurrentConnectionType) {
516      case GCT_FBUS:
517
518 #ifdef DEBUG
519        fprintf(stdout, _("Setting cable for FBUS communication...\n"));
520 #endif /* DEBUG */
521
522        device_changespeed(115200);
523    
524        /* Colin wrote:
525        The data suite cable has some electronics built into the connector. This of
526        course needs a power supply of some sorts to operate properly.
527
528        In this case power is drawn off the handshaking lines of the PC. DTR has to
529        be set and RTS have to be cleared, thus if you use a terminal program (that
530        does not set the handshaking lines to these conditions) you will get weird
531        results. It will not set them like this since if Request To Send (RTS) is
532        not set the other party will not send any data (in hardware handshaking)
533        and if DTS is not set (handshaking = none) the cable will not receive
534        power. */
535        /* clearing the RTS bit and setting the DTR bit*/
536        device_setdtrrts(1, 0);
537
538        break;
539      case GCT_DLR3:
540
541 #ifdef DEBUG
542        fprintf(stdout, _("Setting DLR3 cable for FBUS communication...\n"));
543 #endif /* DEBUG */
544
545        /* There are 2 ways to init DLR in FBUS: Here is first described by
546           Reuben Harris [reuben.harris@snowvalley.com] and used in Logo Manager,
547             1. Firstly set the connection baud to 19200, DTR off, RTS off,
548                Parity on, one stop bit, 
549             2. Send "AT\r\n". The response should be "AT\r\n\r\nOK\r\n".
550             3. Send "AT&F\r\n". The response should be "AT&F\r\n\r\nOK\r\n".
551             4. Send "AT*NOKIAFBUS\r\n". The response should be
552                "AT*NOKIAFBUS\r\n\r\nOK\r\n".
553             5. Set speed to 115200 
554           This seems to be compatible with more phones*/
555
556        device_changespeed(19200);
557
558        /*leave RTS low, DTR low for duration of session.*/
559        device_setdtrrts(0, 0);
560                             
561        Protocol->WritePhone (3,req );usleep(300);
562        Protocol->WritePhone (5,req2);usleep(300);
563        Protocol->WritePhone(13,req3);usleep(300);
564
565        device_changespeed(115200);
566
567 //     /*  Second method for DLR3:
568 //         Used by some 7110 soft, but not compatible with some other
569 //         phones supporting DLR3 - 7160, NCP2.0*/
570 //     device_changespeed(115200);
571 //     /*leave RTS low, DTR low for duration of session.*/
572 //     device_setdtrrts(0, 0);      
573 //     usleep(100000);
574
575        CurrentConnectionType=GCT_FBUS;
576
577        break;
578      case GCT_Infrared:
579        /* It's more complicated and not done here */
580        break;
581
582      case GCT_Tekram:
583        /* It's more complicated and not done here */
584        break;
585
586      default:
587 #ifdef DEBUG
588        fprintf(stdout,_("Wrong connection type for fbus module. Inform marcin-wiacek@topnet.pl about it\n"));
589 #endif
590        break;
591   }
592
593   return (true);
594 }
595
596 /* Initialise variables and state machine. */
597 GSM_Error FBUS_Initialise(char *port_device, char *initlength,
598                           GSM_ConnectionType connection,
599                           void (*rlp_callback)(RLP_F96Frame *frame))
600 {
601
602   if (!StartConnection (port_device,false,connection))
603     return GE_INTERNALERROR;
604       
605   CurrentConnectionType = connection;
606
607   if (FBUS_OpenSerial() != true) return GE_INTERNALERROR;
608
609   return (GE_NONE);
610 }