This commit was manufactured by cvs2svn to create branch 'uc'.
[gnokii.git] / common / protocol / mbus.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 MBUS protocol
10
11 */
12
13 /* "Turn on" prototypes in MBUS.h */
14 #define __MBUS_c 
15
16 /* System header files */
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21
22 #ifdef WIN32
23   #include <windows.h>
24   #include "misc_win32.h"
25 #else
26   #include <ctype.h>
27 #endif
28  
29 /* Various header file */
30 #include "devices/device.h"
31 #include "gsm-api.h"
32 #include "protocol/mbus.h"
33 #include "protocol/fbus.h"
34
35 GSM_Protocol MBUS_Functions = {
36   MBUS_Initialise,
37   MBUS_SendMessage,
38   MBUS_SendFrame,
39   MBUS_WritePhone,
40   MBUS_Terminate,
41   MBUS_RX_StateMachine
42 };
43
44 /* Local variables */
45 enum FBUS_RX_States RX_State;
46
47 u8 MessageDestination, MessageSource;
48
49 u16 BufferCount;
50
51 u16 MessageLength;
52
53 u8 MessageType;
54
55 u8 MessageBuffer[MBUS_MAX_RECEIVE_LENGTH * 6];
56
57 static u8 RequestSequenceNumber = 0x00;
58   
59 #ifdef DEBUG    
60 char *MBUS_PrintDevice(int Device)
61 {
62   switch (Device) {
63
64   case FBUS_DEVICE_PHONE:
65     return _("Phone");
66
67   case MBUS_DEVICE_PC1:
68     return _("PC");
69
70   case MBUS_DEVICE_PC2:
71     return _("PC");
72
73   default:
74     return _("Unknown");
75
76   }
77 }
78
79 /* N61_RX_DisplayMessage is called when a message we don't know about is
80    received so that the user can see what is going back and forth, and perhaps
81    shed some more light/explain another message type! */
82 void MBUS_RX_DisplayMessage()
83 {
84   fprintf(stdout, _("Msg Dest: %s\n"), MBUS_PrintDevice(MessageDestination));
85   fprintf(stdout, _("Msg Source: %s\n"), MBUS_PrintDevice(MessageSource));
86   fprintf(stdout, _("Msg Type: %02x\n"), MessageType);
87
88   hexdump(MessageLength,MessageBuffer);
89 }
90
91 #endif /* DEBUG */
92
93 /* Prepares the message header and sends it, prepends the message start byte
94            (0x1e) and other values according the value specified when called.
95            Calculates checksum and then sends the lot down the pipe... */
96 int MBUS_SendFrame(u16 message_length, u8 message_type, u8 *buffer) {
97   u8 out_buffer[MBUS_MAX_CONTENT_LENGTH + 2];
98   
99   int count, current=0;
100   unsigned char checksum;
101
102   /* FIXME - we should check for the message length ... */
103
104   /* Now construct the message header. */
105
106   out_buffer[current++] = MBUS_FRAME_ID;    /* Start of the frame indicator */
107
108   out_buffer[current++] = FBUS_DEVICE_PHONE; /* Destination */
109
110   out_buffer[current++] = MBUS_DEVICE_PC1;    /* Source */
111
112   out_buffer[current++] = message_type; /* Type */
113
114   out_buffer[current++] = (message_length-1)/256; /* Length1 */
115   out_buffer[current++] = (message_length-1)%256; /* Length2 */
116
117   /* Copy in data if any. */    
118         
119   if (message_length != 0) {
120     memcpy(out_buffer + current, buffer, message_length);
121     current+=message_length;
122   }
123
124   /* Now calculate checksum over entire message and append to message. */
125
126   /* All bytes */
127   checksum = 0;
128   for (count = 0; count < current; count++)
129     checksum ^= out_buffer[count];
130
131   out_buffer[current++] = checksum;
132
133 #ifdef DEBUG
134   NULL_TX_DisplayMessage(current, out_buffer);
135 #endif /* DEBUG */
136
137   /* Send it out... */
138   if (!MBUS_WritePhone(current,out_buffer))
139     return (false);
140
141   return (true);
142 }
143
144 int MBUS_SendMessage(u16 message_length, u8 message_type, u8 *buffer) {
145   
146   u8 frame_buffer[MBUS_MAX_CONTENT_LENGTH + 2];
147
148     RequestSequenceNumber++;
149
150     memcpy(frame_buffer, buffer, message_length);
151     frame_buffer[message_length] = RequestSequenceNumber;  
152     MBUS_SendFrame(message_length + 1, message_type, frame_buffer);
153
154   return (true);
155 }
156
157 int MBUS_SendAck(u8 message_type, u8 message_seq) {
158
159   unsigned char request[6];
160
161   int count;
162
163   request[0]=MBUS_FRAME_ID;
164   request[1]=FBUS_DEVICE_PHONE;
165   request[2]=MBUS_DEVICE_PC1;
166   request[3]=FBUS_FRTYPE_ACK;
167   request[4] = message_seq;
168   request[5]=0;
169
170   /* Checksum */
171   for (count = 0; count < 5; count++)
172     request[5] ^= request[count];
173
174 #ifdef DEBUG
175   fprintf(stdout, _("[Sending Ack of type %02x, seq: %x]\n"), message_type, message_seq);
176
177   NULL_TX_DisplayMessage(5, request);
178 #endif /* DEBUG */
179
180   if (!MBUS_WritePhone(6, request)) {
181 #ifdef DEBUG
182     fprintf(stdout,_("Sending ACK failed %i !\n"),count);
183 #endif
184     return (false);
185   }
186
187   return(true);
188 }
189
190 /* Applications should call MBUS_Terminate to shut down the MBUS thread and
191    close the serial port. */
192 void MBUS_Terminate(void)
193 {  
194   /* Request termination of thread */
195   CurrentRequestTerminate = true;
196
197   /* RTS low */
198   device_setdtrrts(0, 0);
199
200   /* Close serial port. */
201   device_close();
202 }
203
204 /* RX_State machine for receive handling.  Called once for each character
205    received from the phone/phone. */
206 void MBUS_RX_StateMachine(unsigned char rx_byte) {
207
208   static struct timeval time_now, time_last, time_diff;
209   
210   unsigned char max;
211
212   static int checksum[2];
213
214 #if defined(__svr4__) || defined(__FreeBSD__) || defined(DEBUG)
215   int i=0;
216 #endif
217
218 #ifdef DEBUG
219   if (strcmp(GSM_Info->MBUSModels, "sniff"))
220   {
221 #endif
222
223   checksum[0]=checksum[1];
224   checksum[1] ^= rx_byte;
225
226   switch (RX_State) {
227         
228   /* Messages from the phone start with an 0x1f (MBUS).
229      We use this to "synchronise" with the incoming data stream. However,
230      if we see something else, we assume we have lost sync and we require
231      a gap of at least 5ms before we start looking again. This is because
232      the data part of the frame could contain a byte which looks like the
233      sync byte */
234   case FBUS_RX_Discarding:
235 #ifndef VC6
236     gettimeofday(&time_now, NULL);
237     timersub(&time_now, &time_last, &time_diff);
238     if (time_diff.tv_sec == 0 && time_diff.tv_usec < 5000) {
239       time_last = time_now;  /* no gap seen, continue discarding */
240       break;
241     }
242     /* else fall through to... */
243 #endif
244
245   case FBUS_RX_Sync:
246
247     if (rx_byte == MBUS_FRAME_ID) {
248
249       BufferCount = 0;
250         
251       RX_State = FBUS_RX_GetDestination;
252         
253       /* Initialize checksum. */
254       checksum[1] = MBUS_FRAME_ID;
255     } else {
256       /* Lost frame sync */
257       RX_State = FBUS_RX_Discarding;
258 #ifndef VC6
259       gettimeofday(&time_last, NULL);
260 #endif
261     }
262     
263     break;
264
265   case FBUS_RX_GetDestination:
266
267     MessageDestination=rx_byte;
268     RX_State = FBUS_RX_GetSource;
269
270     /* When there is a checksum error and things get out of sync we have to manage to resync */
271     /* If doing a data call at the time, finding a 0x1e etc is really quite likely in the data stream */
272     /* Then all sorts of horrible things happen because the packet length etc is wrong... */
273     /* Therefore we test here for a destination of 0x0c and return to the top if it is not */
274     /* The same testing for MBUS. Only one change: MBUS returns, what we send.
275        So, the byte can be 0x10 (destination MBUS) or 0x00 (phone) */
276     if (rx_byte!=MBUS_DEVICE_PC1 && rx_byte!=MBUS_DEVICE_PC2 && rx_byte!=FBUS_DEVICE_PHONE) {
277       RX_State=FBUS_RX_Sync;
278 #ifdef DEBUG
279       fprintf(stdout,"The mbus stream is out of sync - expected 0x10 or 0x00, got %2x\n",rx_byte);
280 #endif
281     }
282
283     break;
284
285   case FBUS_RX_GetSource:
286
287     MessageSource=rx_byte;
288     RX_State = FBUS_RX_GetType;
289
290     /* Source should be 0x00 or 0x10 */
291     if (rx_byte!=FBUS_DEVICE_PHONE && rx_byte!=MBUS_DEVICE_PC1 && rx_byte!=MBUS_DEVICE_PC2)  {
292       RX_State=FBUS_RX_Sync;
293 #ifdef DEBUG
294       fprintf(stdout,"The mbus stream is out of sync - expected 0x00 or 0x10, got %2x\n",rx_byte);
295 #endif
296     }    
297     
298     break;
299
300   case FBUS_RX_GetType:
301
302     MessageType=rx_byte;
303
304     RX_State = FBUS_RX_GetLength1;
305
306     break;
307
308   case FBUS_RX_GetLength1:
309
310     MessageLength=0;
311
312     /* MW:Here are problems with conversion. For chars 0-127 it's OK, for
313        higher not (probably because rx_byte is char type) - improtant
314        for MBUS. So, I make it double and strange - generally it should be
315        more simple and make simple convert rx_byte into MessageLength */
316     if (rx_byte!=0) {
317       max=rx_byte;
318       MessageLength=max*256;
319     }
320
321     RX_State = FBUS_RX_GetLength2;
322     
323     break;
324     
325   case FBUS_RX_GetLength2:
326
327     /* MW:Here are problems with conversion. For chars 0-127 it's OK, for
328        higher not (probably because rx_byte is char type) - improtant
329        for MBUS. So, I make it double and strange - generally it should be
330        more simple and make simple convert rx_byte into MessageLength */      
331 #if defined(__svr4__) || defined(__FreeBSD__)
332     if (rx_byte!=0) {
333       for (i=0;i<rx_byte;i++)
334         MessageLength=MessageLength++;
335     }
336 #else
337     if (rx_byte!=0) {
338       max=rx_byte;
339       MessageLength=MessageLength+max;
340     }
341 #endif
342     
343     RX_State = FBUS_RX_GetMessage;
344
345     /* In MBUS ACK ends here */
346     if (MessageType==FBUS_FRTYPE_ACK)
347     {
348 #ifdef DEBUG
349       fprintf(stdout, _("[Received Ack from phone]\n"));
350 #endif /* DEBUG */
351       RX_State = FBUS_RX_Sync;
352     }
353     
354     break;
355     
356   case FBUS_RX_GetMessage:
357
358     MessageBuffer[BufferCount] = rx_byte;
359     BufferCount ++;
360
361     /* If this is the last byte, it's the checksum. */
362     if (BufferCount == MessageLength+2) {
363         
364       /* Is the checksum correct? */
365       if (checksum[0] == rx_byte) {
366
367         /* We do not want to send ACK of ACKs and ACK of RLP frames. */
368         if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1
369             && (MessageDestination==MBUS_DEVICE_PC1 || MessageDestination==MBUS_DEVICE_PC2)) {
370           MBUS_SendAck(MessageType, MessageBuffer[BufferCount-2]);
371         }
372
373         /* We don't write info about messages sent to phone */
374         if (MessageDestination!=FBUS_DEVICE_PHONE) {
375 #ifdef DEBUG
376           /* Do not debug Ack and RLP frames to detail. */
377           if (MessageType != FBUS_FRTYPE_ACK && MessageType != 0xf1)
378               MBUS_RX_DisplayMessage();
379 #endif /* DEBUG */
380
381           GSM->DispatchMessage(MessageLength, MessageBuffer, MessageType);
382
383         } else {
384 #ifdef DEBUG
385           if (strstr(GSM_Info->MBUSModels, "6110sniff") != NULL) {
386              fprintf(stdout, _("PC: "));
387
388              fprintf(stdout, "%02x:", MBUS_FRAME_ID);
389              fprintf(stdout, "%02x:", MBUS_DEVICE_PC1);
390              fprintf(stdout, "%02x:", FBUS_DEVICE_PHONE);
391              fprintf(stdout, "%02x:", MessageType);
392              fprintf(stdout, "%02x:", MessageLength/256);
393              fprintf(stdout, "%02x:", MessageLength%256);
394
395 //             NULL_TX_DisplayMessage(current, out_buffer);
396
397              for (i = 0; i < BufferCount; i++)
398                fprintf(stdout, "%02x:", MessageBuffer[i]);
399
400              fprintf(stdout, "\n");
401           }
402 #endif /* DEBUG */
403         }
404       } else {
405 #ifdef DEBUG
406         fprintf(stdout, _("Bad checksum %02x (should be %02x), msg len=%i !\n"),rx_byte,checksum[0],MessageLength);
407 #endif /* DEBUG */
408       }
409       RX_State = FBUS_RX_Sync;
410     }
411     break;
412   }
413
414 #ifdef DEBUG
415
416   } else {
417     if (isprint(rx_byte))
418       fprintf(stdout, "[%02x%c]", rx_byte, rx_byte);
419     else
420       fprintf(stdout, "[%02x ]", rx_byte);
421
422   }
423
424 #endif
425 }
426
427 /* Called by initialisation code to open comm port in asynchronous mode. */
428 bool MBUS_OpenSerial(void)
429 {
430 #ifdef DEBUG
431   fprintf(stdout, _("Setting MBUS communication...\n"));
432 #endif /* DEBUG */
433
434   device_changespeed(9600);
435
436   usleep(100);
437
438   if (strstr(GSM_Info->MBUSModels, "sniff") == NULL) {
439
440     /* leave RTS high, DTR low for duration of session. */
441     device_setdtrrts(0, 1);
442   
443     usleep(100);
444   }
445
446   return (true);
447
448 }
449
450 /* Initialise variables and state machine. */
451 GSM_Error MBUS_Initialise(char *port_device, char *initlength,
452                           GSM_ConnectionType connection,
453                           void (*rlp_callback)(RLP_F96Frame *frame))
454 {
455   if (!StartConnection (port_device,true,connection))
456     return GE_INTERNALERROR;
457     
458   CurrentConnectionType = connection;
459
460   if (MBUS_OpenSerial() != true) {
461     /* Fail so sit here till calling code works out there is a problem. */
462 //    while (!CurrentRequestTerminate)
463 //      usleep (100000);
464           
465     return GE_INTERNALERROR;
466   }
467
468   return (GE_NONE);
469 }
470
471 bool MBUS_WritePhone (u16 length, u8 *buffer) {
472
473   if (!CurrentDisableKeepAlive)
474     usleep(150);
475
476   if (device_write(buffer,length)!=length)
477     return false;
478   else
479     return true;
480 }