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