Found in "gnokii-working" directory, some November-patches version
[gnokii.git] / common / cimd.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) 2001 Jan Kratochvil,
10   based on code by Hugh Blemings & Pavel Janík ml.
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 SMS centers by CIMD and related protococols.
15   See README-CIMD for more details on supported protocols.
16
17   The various routines are prefixed by CIMD.
18
19   $Log$
20   Revision 1.1.1.1  2002/04/03 00:08:03  short
21   Found in "gnokii-working" directory, some November-patches version
22
23
24 */
25
26 #define CIMD_DEBUG 1
27
28 /* System header files */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <limits.h>
35  
36
37 #ifdef WIN32
38
39 #include <windows.h>
40 #include "win32/winserial.h"
41
42 #undef IN
43 #undef OUT
44
45 #define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
46 #define sleep(x) Sleep((x) * 1000)
47 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
48 extern HANDLE hPhone;
49
50 #else
51
52 #define WRITEPHONE(a, b, c) device_write(b, c)
53 #include <unistd.h>
54 #include <termios.h>
55 #include <fcntl.h>
56 #include <ctype.h>
57 #include <signal.h>
58 #include <sys/ioctl.h>
59 #include <sys/types.h>
60 #include <sys/time.h>
61 #include <pthread.h>
62 #include <errno.h>
63 #include "device.h"
64 #include "devices/unixserial.h"
65
66 #endif
67
68 /* Various header file */
69
70 #include "config.h"
71 #include "misc.h"
72 #include "gsm-common.h"
73 #include "cfgreader.h"
74
75 /* Global variables used by code in gsm-api.c to expose the functions
76    supported by this model of phone. */
77
78
79 #if __unices__
80 /* fd opened in device.c */
81 extern int device_portfd;
82 #endif
83
84 /* Our private defines */
85
86 /* Define if plain (non-Bin) Submit is needed for 7bit messages
87  */
88 #define CIMD_SUBMIT_7BIT 1
89
90 /* When now catchbuffer was provided and must have some space to decode
91  * OK/ERROR/... result codes.
92  */
93 #define CIMD_CATCHBUFFER_LENGTH 0x400
94
95 /* +1 of maximum position number of CIMD protocol parameter */
96 #define CIMD_PARAMSLOTS (0x10)
97
98 /* We assume the 'right one' class is 1 (Mobile Equipment specific) */
99 #define DEFAULT_CLASS 1
100
101 /* CIMD_Param_Nak_Error codes:
102  */
103 #define CIMD_NAK_NO_SMS (0x5001)
104 #define CIMD_NAK_KEEPALIVE_REPLY (0x9998)
105 #define CIMD_NAK_RESEND (0x9999)
106
107 /* Local variables */
108
109 #ifndef WIN32
110 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
111 #endif
112 static bool RequestTerminate;
113
114
115 static u8 CIMD_CatchBuffer[CIMD_CATCHBUFFER_LENGTH];
116 static u8 *CIMD_CatchBufferPtr=CIMD_CatchBuffer;        /* current destination writing ptr */
117 static GSM_Error *CIMD_CatchBufferErrorP;
118
119 static void CIMD_CatchBufferStart(GSM_Error *errorcodep);
120
121
122 #define CIMD_MARK_START (0x02)
123 #define CIMD_MARK_STOP  (0x03)
124 #define CIMD_MARK_SEP   (0x09)
125 #define CIMD_MARK_SEPS  "\x09"
126
127 /* ELF=ELement Format */
128 enum CIMD_ELF {
129         CIMD_ELF_HexByte,
130         CIMD_ELF_HexWord,
131         CIMD_ELF_Decimal,
132         CIMD_ELF_StringZ,
133         CIMD_ELF_HexBlock,
134         CIMD_ELF_Empty,
135         };
136
137 struct CIMD_Param {
138         u16 code;
139         enum CIMD_ELF elf;
140         };
141
142 enum CIMD_Param_SAMPLE {                /* we need some universal enum for typecasting all params */
143         CIMD_Param_SAMPLE_dummy,
144         };
145
146 /* CIMD_Cmd_Ack:
147  */
148 enum CIMD_Param_Ack {
149         CIMD_Param_Ack_Cmd,
150         CIMD_Param_Ack_Error,
151         CIMD_Param_Ack_NULL
152         };
153 static const struct CIMD_Param CIMD_Param_BIP_Ack[]={
154         { /* CIMD_Param_Ack_Cmd   */ 1,CIMD_ELF_HexByte },
155         { /* CIMD_Param_Ack_Error */ 2,CIMD_ELF_StringZ },      /* protocol version on Cmd_Login, otherwise 0x0000 */
156         };
157
158 /* CIMD_Cmd_Nak:
159  */
160 enum CIMD_Param_Nak {
161         CIMD_Param_Nak_Cmd,
162         CIMD_Param_Nak_Error,
163         CIMD_Param_Nak_NULL
164         };
165 static const struct CIMD_Param CIMD_Param_BIP_Nak[]={
166         { /* CIMD_Param_Nak_Cmd   */ 1,CIMD_ELF_HexByte },
167         { /* CIMD_Param_Nak_Error */ 2,CIMD_ELF_HexWord },
168         };
169
170 /* CIMD_Cmd_Login:
171  */
172 enum CIMD_Param_Login {
173         CIMD_Param_Login_ID,
174         CIMD_Param_Login_PWD,
175         CIMD_Param_Login_NULL
176         };
177 static const struct CIMD_Param CIMD_Param_BIP_Login[]={
178         { /* CIMD_Param_Login_ID  */ 1,CIMD_ELF_StringZ },
179         { /* CIMD_Param_Login_PWD */ 2,CIMD_ELF_StringZ },
180         };
181
182 /* CIMD_Cmd_Logout:
183  */
184 enum CIMD_Param_Logout {
185         CIMD_Param_Logout_NULL
186         };
187 static const struct CIMD_Param CIMD_Param_BIP_Logout[]={
188         };
189
190 /* CIMD_Cmd_Retrieve:
191  */
192 enum CIMD_Param_Retrieve {
193         CIMD_Param_Retrieve_NULL
194         };
195 static const struct CIMD_Param CIMD_Param_BIP_Retrieve[]={
196         };
197
198 /* CIMD_Cmd_RetrieveReply:
199  */
200 enum CIMD_Param_RetrieveReply {
201         CIMD_Param_RetrieveReply_Destination,
202         CIMD_Param_RetrieveReply_SourceApplication,     /* ??? */
203         CIMD_Param_RetrieveReply_Text,
204         CIMD_Param_RetrieveReply_Timestamp,
205         CIMD_Param_RetrieveReply_Is8bit,                /* protocol version 1.14+, CIMD_Param_DCSEnable_Type_*DCS* reqd */
206         CIMD_Param_RetrieveReply_PID_DCS,               /* protocol version 1.15+, CIMD_Param_DCSEnable_Type_*PID_DCS* reqd */
207         CIMD_Param_RetrieveReply_SPEC,                  /* protocol version 1.16+, CIMD_Param_DCSEnable_Type_*SPEC* reqd */
208         CIMD_Param_RetrieveReply_NULL
209         };
210 static const struct CIMD_Param CIMD_Param_BIP_RetrieveReply[]={
211         { /* CIMD_Param_RetrieveReply_Destination       */ 1,CIMD_ELF_StringZ },
212         { /* CIMD_Param_RetrieveReply_SourceApplication */ 2,CIMD_ELF_StringZ },
213         { /* CIMD_Param_RetrieveReply_Text              */ 3,CIMD_ELF_StringZ },
214         { /* CIMD_Param_RetrieveReply_Timestamp         */ 4,CIMD_ELF_StringZ },
215         { /* CIMD_Param_RetrieveReply_Is8bit            */ 5,CIMD_ELF_Decimal },
216         { /* CIMD_Param_RetrieveReply_PID_DCS           */ 6,CIMD_ELF_Decimal }, /* upper=PID, lower=DCS, FIXME: should be HexWord! */
217         { /* CIMD_Param_RetrieveReply_SPEC              */ 7,CIMD_ELF_Decimal }, /* FIXME: should be HexByte! */
218         };
219
220 /* CIMD_Cmd_Count:
221  */
222 enum CIMD_Param_Count {
223         CIMD_Param_Count_NULL
224         };
225 static const struct CIMD_Param CIMD_Param_BIP_Count[]={
226         };
227
228 /* CIMD_Cmd_CountReply:
229  */
230 enum CIMD_Param_CountReply {
231         CIMD_Param_CountReply_Used,             /* Used==NotRead==Slots */
232         CIMD_Param_CountReply_NULL
233         };
234 static const struct CIMD_Param CIMD_Param_BIP_CountReply[]={
235         { /* CIMD_Param_CountReply_Used */ 1,CIMD_ELF_Decimal },
236         };
237
238 /* CIMD_Cmd_Submit:
239  */
240 enum CIMD_Param_Submit {
241         CIMD_Param_Submit_Destination,
242         CIMD_Param_Submit_Text,
243         CIMD_Param_Submit_ValidityPeriod,
244         CIMD_Param_Submit_AUX,          /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
245         CIMD_Param_Submit_DCS,          /* protocol version 1.14+ */
246         CIMD_Param_Submit_PID,          /* protocol version 1.15+ */
247         CIMD_Param_Submit_SPEC,         /* protocol version 1.16+ */
248         CIMD_Param_Submit_NULL
249         };
250 static const struct CIMD_Param CIMD_Param_BIP_Submit[]={
251         { /* CIMD_Param_Submit_Destination    */ 1,CIMD_ELF_StringZ },
252         { /* CIMD_Param_Submit_Text           */ 2,CIMD_ELF_StringZ },
253         { /* CIMD_Param_Submit_ValidityPeriod */ 3,CIMD_ELF_HexByte },
254         { /* CIMD_Param_Submit_AUX            */ 4,CIMD_ELF_Empty   },
255         { /* CIMD_Param_Submit_DCS            */ 5,CIMD_ELF_Decimal },
256         { /* CIMD_Param_Submit_PID            */ 6,CIMD_ELF_HexByte },
257         { /* CIMD_Param_Submit_SPEC           */ 7,CIMD_ELF_HexByte },
258         };
259
260 /* CIMD_Cmd_SubmitBin:          protocol version 1.12+
261  */
262 enum CIMD_Param_SubmitBin {
263         CIMD_Param_SubmitBin_Destination,
264         CIMD_Param_SubmitBin_Text,
265         CIMD_Param_SubmitBin_ValidityPeriod,
266         CIMD_Param_SubmitBin_AUX,               /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
267         CIMD_Param_SubmitBin_DCS,               /* protocol version 1.14+ */
268         CIMD_Param_SubmitBin_PID,               /* protocol version 1.15+ */
269         CIMD_Param_SubmitBin_SPEC,              /* protocol version 1.16+ */
270         CIMD_Param_SubmitBin_NULL
271         };
272 static const struct CIMD_Param CIMD_Param_BIP_SubmitBin[]={
273         { /* CIMD_Param_SubmitBin_Destination    */ 1,CIMD_ELF_StringZ },
274         { /* CIMD_Param_SubmitBin_Text           */ 2,CIMD_ELF_StringZ },
275         { /* CIMD_Param_SubmitBin_ValidityPeriod */ 3,CIMD_ELF_HexByte },
276         { /* CIMD_Param_SubmitBin_AUX            */ 4,CIMD_ELF_Empty   },
277         { /* CIMD_Param_SubmitBin_DCS            */ 5,CIMD_ELF_Decimal },
278         { /* CIMD_Param_SubmitBin_PID            */ 6,CIMD_ELF_HexByte },
279         { /* CIMD_Param_SubmitBin_SPEC           */ 7,CIMD_ELF_HexByte },
280         };
281
282 /* CIMD_Cmd_DCSEnable:          protocol version 1.14+
283  */
284 enum CIMD_Param_DCSEnable {
285         CIMD_Param_DCSEnable_Type,
286         CIMD_Param_DCSEnable_NULL
287         };
288 static const struct CIMD_Param CIMD_Param_BIP_DCSEnable[]={
289         { /* CIMD_Param_DCSEnable_Type */ 1,CIMD_ELF_HexWord },
290         };
291
292 enum CIMD_Param_DCSEnable_Type {
293         CIMD_Param_DCSEnable_Type_None        =0x0000,  /* protocol version 1.14+ */
294         CIMD_Param_DCSEnable_Type_DCS         =0x0001,  /* protocol version 1.14+ */
295         CIMD_Param_DCSEnable_Type_PID_DCS     =0x0002,  /* protocol version 1.15+ */
296         CIMD_Param_DCSEnable_Type_PID_DCS_SPEC=0x0003,  /* protocol version 1.16+ */
297         };
298
299 #define CIMD_PARAM_ENTRY(param) (param),ARRAY_LEN((param))
300
301 struct CIMD_Cmd {
302         u8 code;
303         const struct CIMD_Param *param;
304         unsigned paramcnt;
305         };
306
307 enum CIMD_Cmd_Type {
308         CIMD_Cmd_Ack,
309         CIMD_Cmd_Nak,
310         CIMD_Cmd_Login,
311         CIMD_Cmd_Logout,
312         CIMD_Cmd_Submit,
313         CIMD_Cmd_Retrieve,
314         CIMD_Cmd_RetrieveReply,
315         CIMD_Cmd_Count,
316         CIMD_Cmd_CountReply,
317         CIMD_Cmd_SubmitBin,             /* protocol version 1.12+ */
318         CIMD_Cmd_DCSEnable,             /* protocol version 1.14+ */
319         CIMD_Cmd_NULL           /* stdarg termination, MUST be last! */
320         };
321 static const struct CIMD_Cmd CIMD_Cmd_BIP[]={
322         { /* CIMD_Cmd_Ack           */ 0x00,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Ack          ) },
323         { /* CIMD_Cmd_Nak           */ 0x99,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Nak          ) },
324         { /* CIMD_Cmd_Login         */ 0x01,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Login        ) },
325         { /* CIMD_Cmd_Logout        */ 0x02,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Logout       ) },
326         { /* CIMD_Cmd_Submit        */ 0x03,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Submit       ) },
327         { /* CIMD_Cmd_Retrieve      */ 0x05,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Retrieve     ) },
328         { /* CIMD_Cmd_RetrieveReply */ 0x06,CIMD_PARAM_ENTRY(CIMD_Param_BIP_RetrieveReply) },
329         { /* CIMD_Cmd_Count         */ 0x51,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Count        ) },
330         { /* CIMD_Cmd_CountReply    */ 0x61,CIMD_PARAM_ENTRY(CIMD_Param_BIP_CountReply   ) },
331         { /* CIMD_Cmd_SubmitBin     */ 0x31,CIMD_PARAM_ENTRY(CIMD_Param_BIP_SubmitBin    ) },
332         { /* CIMD_Cmd_DCSEnable     */ 0x53,CIMD_PARAM_ENTRY(CIMD_Param_BIP_DCSEnable    ) },
333         };
334
335 static const struct CIMD_Cmd *CIMD_Cmd=CIMD_Cmd_BIP;    /* FIXME: When other protocols get supported... */
336 static unsigned CIMD_Cmdcnt=ARRAY_LEN(CIMD_Cmd_BIP);    /* FIXME: When other protocols get supported... */
337
338 struct CIMD_Param_Ack_Nak_Error {
339         u16 code;
340         const char *msg;
341         };
342
343 static const struct CIMD_Param_Ack_Nak_Error CIMD_Param_Ack_Nak_Error[]={
344         /* CIMD_Cmd_Login */
345         { 0x1101,N_("User is already logged in") },
346         { 0x1001,N_("Logging in is currently disabled") },
347         { 0x9999,N_("Username not found") },
348                 /* 0x000? can be bitmask combined from: */
349         { 0x0001,N_("Invalid password") },
350         { 0x0002,N_("User has forbidden access") },
351         { 0x0008,N_("User is already registered") },            /* ??? difference from 0x1101 ? */
352         /* CIMD_Cmd_Retrieve / CIMD_Cmd_Count */
353         { 0x5001,N_("No SMS found or not detectable") },        /* ==CIMD_NAK_NO_SMS */
354         { 0x5011,N_("Function not enabled") },
355         /* CIMD_Cmd_Retrieve */
356         { 0x5021,N_("Automatical retrieve is active") },
357         /* CIMD_Cmd_Submit / CIMD_Cmd_SubmitBin */
358         { 0x3001,N_("Error during processing SMS in BMG") },
359         { 0x3061,N_("Destination number has invalid format") },
360         /* CIMD_Cmd_SubmitBin */
361         { 0x3051,N_("Invalid DCS") },           /* protocol version 1.14+ */
362         { 0x3081,N_("Invalid Text") },          /* protocol version 1.14+ */
363         { 0x3091,N_("SMS is empty") },          /* protocol version 1.14+ */
364         { 0x3099,N_("SMS submit too fast") },   /* protocol version 1.16+ */
365         };
366
367
368 static struct CIMD_paramslot CIMD_RX_Packet_slot[CIMD_PARAMSLOTS];
369 #define CIMD_RX_PACKET_PARAMSLOT(paramsample) (CIMD_RX_Packet_slot+1+(unsigned)(paramsample))
370 static enum CIMD_Cmd_Type CIMD_RX_Packet_Cmd;
371 static unsigned CIMD_RX_Packet_slots;
372
373
374 /* RETURNS: Processed */
375 typedef bool (*CIMD_RX_PatrolFunc)(void);
376 typedef void (*CIMD_RX_PatrolReset)(void);
377
378 struct CIMD_RX_Patrol {
379         enum CIMD_Cmd_Type cmd;
380         CIMD_RX_PatrolFunc func;
381         CIMD_RX_PatrolReset reset;
382         };
383
384 static const struct CIMD_RX_Patrol *CIMD_RX_Patrol_Current;
385
386 static char *CIMD_RX_Patrol_Ack_Error;
387 static u16 CIMD_RX_Patrol_Nak_Error;
388
389
390 #ifndef WIN32
391
392 static pthread_t Thread;
393 # if __unices__
394 static pthread_t selThread;
395 # endif
396
397 #endif
398
399 /* Local variables used by get/set phonebook entry code. Buffer is used as a
400    source or destination for phonebook data and other functions... Error is
401    set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
402    handler routines as appropriate. */
403
404 static GSM_SMSMessage     *CurrentSMSMessage;
405 static GSM_Error          CurrentSMSMessageError;
406
407 static GSM_SMSStatus      *CurrentSMSStatus;
408 static GSM_Error          CurrentSMSStatusError;
409
410 static unsigned char      Revision[GSM_MAX_REVISION_LENGTH];
411 static unsigned char      Model   [GSM_MAX_MODEL_LENGTH];
412
413
414 static u8 CIMD_TX_SendCommand_buf[sizeof(CIMD_CatchBuffer)];    /* sizeof() is not required to be == but it is appropriate */
415 static u8 *CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
416
417 struct CIMD_paramslot {
418         u16 code;
419         enum CIMD_ELF elf;
420         union {
421                 struct { u8    i;               } HexByte;
422                 struct { u16   i;               } HexWord;
423                 struct { int   i;               } Decimal;
424                 struct { char *s;               } StringZ;
425                 struct { u8   *buf; size_t len; } HexBlock;
426                 struct { int _dummy;            } Empty;
427                 } u;
428         };
429
430 /* slots are sorted by moving the whole blocks but they are small so it is OK
431  */
432 static struct CIMD_paramslot CIMD_TX_SendCommand_paramslots[CIMD_PARAMSLOTS];
433 static int CIMD_TX_SendCommand_paramslots_full;
434 static enum CIMD_Cmd_Type CIMD_TX_SendCommand_Cmd;
435
436 /* RETURNS: Success
437  */
438 static bool CIMD_TX_SendCommand_vpushparam(u16 code,enum CIMD_ELF elf,va_list *app)
439 {
440 struct CIMD_paramslot *slot=CIMD_TX_SendCommand_paramslots+(CIMD_TX_SendCommand_paramslots_full++);
441
442         if (slot>=CIMD_TX_SendCommand_paramslots+ARRAY_LEN(CIMD_TX_SendCommand_paramslots)) {
443                 /* assertion */
444                 fprintf(stderr,"Out of param slots!\n");
445                 return(false);
446                 }
447         slot->code=code;
448         slot->elf=elf;
449         switch (elf) {
450                 case CIMD_ELF_HexByte:
451                         slot->u.HexByte.i=va_arg((*app),int);
452                         break;
453                 case CIMD_ELF_HexWord:
454                         slot->u.HexWord.i=va_arg((*app),int);
455                         break;
456                 case CIMD_ELF_Decimal:
457                         slot->u.Decimal.i=va_arg((*app),int);
458                         break;
459                 case CIMD_ELF_StringZ:
460                         slot->u.StringZ.s=va_arg((*app),char *);
461                         break;
462                 case CIMD_ELF_HexBlock:
463                         slot->u.HexBlock.buf=va_arg((*app),u8 *);
464                         slot->u.HexBlock.len=va_arg((*app),size_t);
465                         break;
466                 case CIMD_ELF_Empty:
467                         break;
468                 }
469         return(true);
470 }
471
472 /* RETURNS: Success
473  */
474 static bool CIMD_TX_SendCommand_pushparam(u16 code,enum CIMD_ELF elf,...)
475 {
476 va_list ap;
477 bool r;
478
479         va_start(ap,elf);
480         r=CIMD_TX_SendCommand_vpushparam(code,elf,&ap);
481         va_end(ap);
482         return(r);
483 }
484
485 static int CIMD_TX_SendCommand_paramslots_compare
486         (const struct CIMD_paramslot *a,const struct CIMD_paramslot *b)
487 {
488         return((b->code<a->code)-(a->code<b->code));
489 }
490
491 /* RETURNS: Success
492  */
493 static bool CIMD_TX_SendCommand_storeparam(struct CIMD_paramslot *slot)
494 {
495         switch (slot->elf) {
496                 case CIMD_ELF_HexByte:
497                         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",slot->u.HexByte.i);
498                         break;
499                 case CIMD_ELF_HexWord:
500                         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%04X",slot->u.HexWord.i);
501                         break;
502                 case CIMD_ELF_Decimal:
503                         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%d",slot->u.Decimal.i);
504                         break;
505                 case CIMD_ELF_StringZ: {
506 size_t len;
507                         if (!slot->u.StringZ.s)         /* NULL is interpreted as "" */
508                                 break;
509                         len=strlen(slot->u.StringZ.s);
510                         if (CIMD_TX_SendCommand_buf_d+len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
511                                 return(false);          /* error - overflow */
512                         memcpy(CIMD_TX_SendCommand_buf_d,slot->u.StringZ.s,len);
513                         CIMD_TX_SendCommand_buf_d+=len;
514                         } break;
515                 case CIMD_ELF_HexBlock:
516                         if (CIMD_TX_SendCommand_buf_d+2*slot->u.HexBlock.len
517                                         > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
518                                 return(false);          /* error - overflow */
519                         CIMD_TX_SendCommand_buf_d=SMS_BlockToHex(CIMD_TX_SendCommand_buf_d,
520                                         slot->u.HexBlock.buf,slot->u.HexBlock.len);     /* never fails */
521                         break;
522                 case CIMD_ELF_Empty:
523                         break;
524                 }
525         return(true);   /* success */
526 }
527
528 static unsigned CIMD_KeepAlives=0;
529 static pthread_mutex_t CIMD_KeepAlivesLock;
530
531 /* This function is NOT thread-safe!
532  */
533 static void CIMD_KeepAlivesLockInit(void)
534 {
535 static bool done=false;
536
537         if (done)
538                 return;
539         pthread_mutex_init(&CIMD_KeepAlivesLock,NULL);
540         done=true;
541 }
542
543 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...);
544
545 static void CIMD_KeepAlivesCheck(void)
546 {
547 unsigned alives;
548
549         CIMD_KeepAlivesLockInit();
550         pthread_mutex_lock(&CIMD_KeepAlivesLock);
551         alives=CIMD_KeepAlives;
552         CIMD_KeepAlives=0;
553         pthread_mutex_unlock(&CIMD_KeepAlivesLock);
554
555         while (alives) {
556                 CIMD_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,
557                         CIMD_Cmd_Nak,
558                                 CIMD_Param_Nak_Cmd,(CIMD_Cmd+(unsigned)CIMD_Cmd_Ack)->code,     /* ==0x00 */
559                                 CIMD_Param_Nak_Error,CIMD_NAK_KEEPALIVE_REPLY,                  /* ==0x9998 */
560                                 CIMD_Param_Nak_NULL);
561                 alives--;
562                 }
563 }
564
565 /* RETURNS: Success
566  */
567 static bool CIMD_TX_SendCommandResend(boid)
568 {
569 int writephone_got;
570
571         writephone_got=WRITEPHONE(PortFD, CIMD_TX_SendCommand_buf, CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf);
572
573 #ifdef CIMD_DEBUG
574         write(1,"CMD:<STX>",9);
575         write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1));
576         write(1,"<ETX>\n",6);
577 #endif
578
579         return(writephone_got==(CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf));
580 }
581
582 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...)
583 {
584 va_list ap;
585 const struct CIMD_Cmd *cmdstruct;
586 enum CIMD_Param_SAMPLE param;
587 const struct CIMD_Param *paramstruct;
588 u8 *u8s,xsum;
589 struct CIMD_paramslot *slot;
590 int parami;
591
592         CIMD_KeepAlivesCheck();
593
594         va_start(ap,cmd);
595         if (errorcodep)
596                 *errorcodep=GE_BUSY;
597         CIMD_RX_Patrol_Current=patrol;
598
599         CIMD_TX_SendCommand_Cmd=cmd;
600         CIMD_TX_SendCommand_paramslots_full=0;
601         if ((unsigned)cmd>=CIMD_Cmdcnt)
602                 goto fail;              /* assert */
603         cmdstruct=CIMD_Cmd+(unsigned)cmd;
604
605         if (!CIMD_TX_SendCommand_pushparam(0/*code*/,CIMD_ELF_HexByte/*elf*/,cmdstruct->code))
606                 goto fail;
607         for (;;) {
608                 param=va_arg(ap,enum CIMD_Param_SAMPLE);
609                 if ((unsigned)param==cmdstruct->paramcnt)
610                         break;          /* CIMD_Param_*_NULL reached */
611                 if ((unsigned)param>cmdstruct->paramcnt)
612                         goto fail;      /* assert */
613                 paramstruct=(cmdstruct->param+(unsigned)param);
614                 if (!CIMD_TX_SendCommand_vpushparam(paramstruct->code,paramstruct->elf,&ap))
615                         goto fail;
616                 }
617         qsort(CIMD_TX_SendCommand_paramslots,CIMD_TX_SendCommand_paramslots_full,
618                         sizeof(*CIMD_TX_SendCommand_paramslots),
619                         (int (*)(const void *,const void *))CIMD_TX_SendCommand_paramslots_compare);
620
621         CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
622         *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_START;
623
624         slot=CIMD_TX_SendCommand_paramslots;
625         for (parami=0;slot<CIMD_TX_SendCommand_paramslots+CIMD_TX_SendCommand_paramslots_full;parami++) {
626                 if (CIMD_TX_SendCommand_buf_d +32/*safety*/ > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) {
627                                 /* error - overflow */
628 fail:
629                         if (errorcodep)
630                                 *errorcodep=GE_INTERNALERROR;
631                         return;
632                         }
633                 if (slot->code==parami) {
634                         if (!CIMD_TX_SendCommand_storeparam(slot))      /* error */
635                                 goto fail;
636                         }
637                 if (slot->code>=parami)
638                         *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_SEP;
639                 if (slot->code<=parami)
640                         slot++;
641                 }
642         xsum=0;
643         for (u8s=CIMD_TX_SendCommand_buf;u8s<CIMD_TX_SendCommand_buf_d;u8s++)
644                 xsum+=*u8s;
645         CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",(unsigned)xsum);
646         *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_STOP;
647
648         CIMD_CatchBufferStart(errorcodep);
649
650         if (!CIMD_TX_SendCommandResend())
651                 goto fail;
652
653         /* success but we don't wait for the result code */
654 }
655
656 /* This function is used to get storage status from the phone. It currently
657    supports two different memory areas - internal and SIM. */
658
659 static GSM_Error 
660 wait_on(volatile GSM_Error *what, int timeout)
661 {
662 GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
663
664         while (timeout && ((r=*what)==GE_BUSY)) {
665                 if (!--timeout) {
666                         r=GE_TIMEOUT;
667                         break;
668                         }
669                 CIMD_KeepAlivesCheck();
670                 usleep(100000);
671         }
672         /* any specific patrollers are no longer valid */
673         CIMD_RX_Patrol_Current=NULL;
674
675 #ifdef CIMD_DEBUG
676         printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
677 #endif
678
679         return(r);
680 }
681
682 #define WAIT_ON(what, timeout) \
683         do { \
684                 GSM_Error res = wait_on(what, timeout); \
685                 if (res != GE_NONE) \
686                         return res; \
687         } while (0)
688
689 /* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
690  */
691 #define CIMD_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
692         do { \
693 GSM_Error _CIMD_TX_SENDCOMMAND_WAIT_ON_err,*_CIMD_TX_SENDCOMMAND_WAIT_ON_errp; \
694  \
695                 if (!(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
696                         _CIMD_TX_SENDCOMMAND_WAIT_ON_errp=&_CIMD_TX_SENDCOMMAND_WAIT_ON_err; \
697                 CIMD_TX_SendCommand(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
698                 WAIT_ON(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
699         } while (0)
700
701
702 #define CIMD_ERR_WRAPPER(expr) \
703         do { \
704                 GSM_Error err=(expr); \
705                 if (err!=GE_NONE) \
706                         return(err); \
707         } while (0)
708
709 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
710  */
711 static unsigned char CIMD_SMStoSPEC_BIP_CIMD(GSM_SMSMessage *SMS)
712 {
713         return(0
714                         /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
715                          */
716                         |((!!SMS->ReplyViaSameSMSC)<<1) /*ReplyPath*/
717                         |((!!SMS->UDHPresent)<<2) /*UDH set*/
718                         );
719 }
720
721 static unsigned char CIMD_SMStoDCS(GSM_SMSMessage *SMS)
722 {
723 int class=(SMS->Class==-1 ? DEFAULT_CLASS : SMS->Class);
724
725 #if 0 /* NEVER send 0 for BIP CIMD as it would translate it to F6 !!!
726        */
727         if (!SMS->EightBit && class==DEFAULT_CLASS)
728                 return(0x00);
729 #endif
730         return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
731 }
732
733 static GSM_Error CIMD_PhoneSetup(void)
734 {
735 #if 1 /* HACK */
736         CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,100/*timeout*/,NULL/*patrol*/,
737                 CIMD_Cmd_Login,
738                         /* NULLs will be interpreted as ""
739                         */
740                         CIMD_Param_Login_ID,CFG_Get(CFG_Info,"CIMD","name"),
741                         CIMD_Param_Login_PWD,CFG_Get(CFG_Info,"CIMD","password"),
742                         CIMD_Param_Login_NULL);
743         if (!CIMD_RX_Patrol_Ack_Error)
744                 Revision[0]='\0';
745         else
746                 SAFE_STRNCPY_SIZEOF(Revision,CIMD_RX_Patrol_Ack_Error);
747
748         CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
749                 CIMD_Cmd_DCSEnable,
750                         CIMD_Param_DCSEnable_Type,(u16)CIMD_Param_DCSEnable_Type_PID_DCS_SPEC,
751                         CIMD_Param_DCSEnable_NULL);
752 #endif
753
754         return(GE_NONE);
755 }
756
757
758 static void CIMD_RX_Char(char rx_byte);
759 static void CIMD_SigHandler(int status);
760 static bool CIMD_OpenSerial(void);
761
762 GSM_Phone phone_cimd;   /* forward declaration */
763
764 /* Initialise variables and state machine. */
765
766 static GSM_Error CIMD_Init(GSM_Data *data, GSM_Statemachine *state)
767 {
768         RequestTerminate = false;
769
770         SAFE_STRNCPY_SIZEOF(Model,data->Model);
771
772         /* Create and start main thread. */
773
774 #ifdef WIN32
775 {
776 int rtn;
777         rtn = ! OpenConnection(State->Link.PortDevice,CIMD_RX_Char,CIMD_KeepAliveProc);
778         if (rtn != 0) {
779                 return(GE_INTERNALERROR);
780 }
781 #else
782         SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
783         if (!CIMD_OpenSerial())
784                 return(GE_INTERNALERROR);
785 #endif
786
787         CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
788
789         return (GE_NONE);
790 }
791
792 #if __unices__
793 /* thread for handling incoming data */
794 void CIMD_SelectLoop()
795 {
796         int err;
797         fd_set readfds;
798         struct timeval timeout;
799
800         FD_ZERO(&readfds);
801         FD_SET(device_portfd, &readfds);
802         /* set timeout to 15 seconds */
803         timeout.tv_sec=15;
804         timeout.tv_usec=0;
805         while (!RequestTerminate) {
806                 err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
807                 /* call singal handler to process incoming data */
808                 if ( err > 0 ) CIMD_SigHandler(0);
809                 else if (err == -1) perror("Error in SelectLoop");
810         }
811 }
812 #endif
813
814 /* Applications should call CIMD_Terminate to shut down the CIMD thread and
815    close the serial port. */
816
817 static GSM_Error CIMD_Terminate(GSM_Data *data, GSM_Statemachine *state)
818 {
819 GSM_Error err;
820
821 #if 1 /* HACK */
822         /* Don't wait too much as we can have already broken link
823          */
824         CIMD_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
825                 CIMD_Cmd_Logout,
826                         CIMD_Param_Logout_NULL);
827         wait_on(&err,20/*timeout*/);            /* errors ignored, of course */
828 #endif
829
830         /* Request termination of thread */
831         RequestTerminate = true;
832
833 #ifndef WIN32
834         /* Now wait for thread to terminate. */
835         pthread_join(Thread, NULL);
836
837         /* Close serial port. */
838         device_close();
839
840 #else
841         CloseConnection();
842 #endif
843
844         return(GE_NONE);
845 }
846
847 /* messagecenter->No" is NOT set as it is input argument */
848 static void CIMD_MessageCenterClear(GSM_MessageCenter *messagecenter)
849 {
850         messagecenter->Name[0]='\0';            /* not present up to Nokia 9110i */
851         messagecenter->Recipient[0]='\0';       /* not present up to Nokia 9110i */
852         messagecenter->Number[0]='\0';          /* default */
853         messagecenter->Format=GSMF_Text;        /* default */
854         messagecenter->Validity=GSMV_72_Hours;  /* default */
855 }
856
857 static bool CIMD_RX_Patrol_CountReply(void)
858 {
859         CurrentSMSStatus->UnRead=/*FALLTHRU*/
860         CurrentSMSStatus->Used  =/*FALLTHRU*/
861         CurrentSMSStatus->Slots =/*FALLTHRU*/
862                         CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_CountReply_Used)->u.Decimal.i;
863
864         CurrentSMSStatusError=GE_NONE;
865         return(true);
866 }
867
868 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_CountReply_struct=
869         { CIMD_Cmd_CountReply,CIMD_RX_Patrol_CountReply };
870
871 static GSM_Error CIMD_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
872 {
873         CurrentSMSStatus = data->SMSStatus;
874         CurrentSMSStatus->UnRead=0;     /* default */
875         CurrentSMSStatus->Used  =0;     /* default */
876         CurrentSMSStatus->Slots =0;     /* default */
877 #if 1 /* HACK */
878         CIMD_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,100/*timeout*/,&CIMD_RX_Patrol_CountReply_struct/*patrol*/,
879                 CIMD_Cmd_Count,
880                         CIMD_Param_Count_NULL);
881 #endif
882
883         return(GE_NONE);
884 }
885
886 static GSM_Error CIMD_GetImei(GSM_Data *data, GSM_Statemachine *state)
887 {
888         /* not supported by the protocol */
889         data->Imei[0]='\0';
890         return (GE_NONE);
891 }
892
893 static GSM_Error CIMD_GetRevision(GSM_Data *data, GSM_Statemachine *state)
894 {
895         if (*Revision) {
896                 strcpy(data->Revision,Revision);
897                 return (GE_NONE);
898         } else return (GE_TRYAGAIN);
899 }
900
901 static GSM_Error CIMD_GetModel(GSM_Data *data, GSM_Statemachine *state)
902 {
903         /* not supported by the protocol */
904         strcpy(data->Model,Model);
905         return (GE_NONE);
906 }
907
908 static GSM_Error CIMD_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
909 {
910         /* not supported by the protocol */
911         data->Imei[0]='\0';
912         return (GE_NONE);
913 }
914
915 static void CIMD_DateTimeSetCurrent(GSM_DateTime *datetime)
916 {
917 time_t current=time(NULL);
918 struct tm *tm=localtime(&current);
919
920         datetime->AlarmEnabled=false;
921
922         datetime->Year  =tm->tm_year+1900;
923         datetime->Month =tm->tm_mon+1;
924         datetime->Day   =tm->tm_mday;
925         datetime->Hour  =tm->tm_hour;
926         datetime->Minute=tm->tm_min;
927         datetime->Second=tm->tm_sec;
928
929         datetime->Timezone=timezone;
930 }
931
932 /* scts=="18.06.1998 22:33:23"
933  */
934 static bool CIMD_SCTStoSMS(GSM_SMSMessage *SMS,const char *scts)
935 {
936 GSM_DateTime *DateTime=&SMS->Time;
937 const char *fmt="%2d.%2d.%4d %2d:%2d:%2d";      /* trailing '\0' IS used! */
938 const char *fs,*ss;
939
940         fs=fmt;
941         ss=scts;
942         do {
943                 while (*fs=='%') {
944 int nums=(*++fs)-'0';
945                         while (nums--)
946                                 if (!isdigit(*ss++))
947                                         return(false);
948                         }
949                 if (*fs++!=*ss)
950                         return(false);
951                 } while (fs[-1]);
952         /* string is completely valid now */
953
954         sscanf(scts,fmt,
955                         &DateTime->Day,
956                         &DateTime->Month,
957                         &DateTime->Year,
958                         &DateTime->Hour,
959                         &DateTime->Minute,
960                         &DateTime->Second);
961         DateTime->Timezone=0;
962
963         return(true);
964 }
965
966 static void CIMD_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
967 {
968         switch ((dcs&0xF0)>>4) {
969                 case 0x0:
970                         switch (dcs&0x0F) {
971                                 case 0x0:
972                                         CurrentSMSMessage->EightBit=false;
973                                         break;
974                                 }
975                         break;
976                 case 0xF:
977                         CurrentSMSMessage->EightBit=!!(dcs&0x04);       /* bit 2 */
978                         CurrentSMSMessage->Class=(dcs&0x03);            /* bits 0 & 1 */
979                         break;
980                 }
981 }
982
983 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
984  */
985 static void CIMD_SPEC_BIP_CIMDtoSMS(GSM_SMSMessage *SMS,u8 spec)
986 {
987         /* Such specification not supported by BIP:
988          */
989         CurrentSMSMessage->Type=GST_MT;
990         CurrentSMSMessage->Status=GSS_NOTSENTREAD;
991
992         /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
993          */
994
995         SMS->UDHPresent=!!(spec&(1<<2));
996
997         SMS->ReplyViaSameSMSC=!!(spec&(1<<1));
998 }
999
1000 static bool CIMD_RX_Patrol_RetrieveReply(void)
1001 {
1002 u16 pid_dcs;    /* upper=PID, lower=DCS */
1003
1004         {
1005 const char *destination=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Destination)->u.StringZ.s;
1006
1007                 if (strlen(destination)+1 > sizeof(CurrentSMSMessage->Sender)) {
1008 fail:
1009                         CurrentSMSMessageError=GE_INTERNALERROR;
1010                         return(true);           /* error */
1011                         }
1012                 strcpy(CurrentSMSMessage->Sender,destination);
1013                 }
1014
1015         if ((CurrentSMSMessage->EightBit=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Is8bit)->u.Decimal.i)) {
1016 const char *hextext=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1017 size_t hextextlen=strlen(hextext);
1018
1019                 if ((hextextlen&1) || (hextextlen/2 > sizeof(CurrentSMSMessage->MessageText)))
1020                         goto fail;              /* error - message too long */
1021                 if (!SMS_BlockFromHex(CurrentSMSMessage->MessageText/*d*/,hextext,hextextlen))
1022                         goto fail;              /* error - parse error */
1023                 CurrentSMSMessage->MessageTextLength=hextextlen/2;
1024                 }
1025         else {  /* 7bit */
1026 const char *text=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1027 size_t textlen=strlen(text);
1028
1029                 if (textlen+1 > sizeof(CurrentSMSMessage->MessageText))
1030                         goto fail;              /* error - message too long */
1031                 strcpy(CurrentSMSMessage->MessageText,text);
1032                 CurrentSMSMessage->MessageTextLength=textlen;
1033                 }
1034
1035         /* errors ignored as it is not fatal */
1036         CIMD_SCTStoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Timestamp)->u.StringZ.s);
1037
1038         pid_dcs=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_PID_DCS)->u.HexWord.i;
1039
1040         /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
1041         CurrentSMSMessage->MessageCenter.Format=(GSM_SMSMessageFormat)(pid_dcs>>8U);    /* <pid> */
1042         CIMD_DCStoSMS(CurrentSMSMessage,(pid_dcs&0x00FFU));
1043         CIMD_SPEC_BIP_CIMDtoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_SPEC)->u.HexByte.i);
1044
1045         if (CurrentSMSMessage->UDHPresent) {
1046 u8 udhlen;
1047                 if (CurrentSMSMessage->MessageTextLength<=0)
1048                         goto fail;
1049                 udhlen=1/*sizeof(udhlen)*/ +CurrentSMSMessage->MessageText[0];
1050                 if (udhlen > CurrentSMSMessage->MessageTextLength)
1051                         goto fail;
1052                 memcpy(CurrentSMSMessage->UDH,CurrentSMSMessage->MessageText,udhlen);
1053                 memmove(CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageText+udhlen,
1054                                 CurrentSMSMessage->MessageTextLength-udhlen +1/*Trailing '\0'*/);
1055                 CurrentSMSMessage->MessageTextLength-=udhlen;
1056                 }
1057
1058         CurrentSMSMessageError=GE_NONE;
1059         return(true);
1060 }
1061
1062 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_RetrieveReply_struct=
1063         { CIMD_Cmd_RetrieveReply,CIMD_RX_Patrol_RetrieveReply };
1064
1065 static GSM_Error CIMD_GetSMS(GSM_Data *data, GSM_Statemachine *state)
1066 {
1067         CurrentSMSMessage = data->SMSMessage;
1068
1069         if (CurrentSMSMessage->MemoryType!=GMT_SM)
1070                 return(GE_INVALIDMEMORYTYPE);
1071
1072         CIMD_DateTimeSetCurrent(&CurrentSMSMessage->Time);              /* default */
1073         CIMD_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime);          /* not present in the protocol */
1074         CurrentSMSMessage->MessageTextLength=0;                         /* default */
1075         CurrentSMSMessage->Validity=72/*hours*/*60;                     /* default */
1076         CurrentSMSMessage->UDHPresent=false;                            /* default */
1077         CurrentSMSMessage->MessageText[0]='\0';                         /* default */
1078
1079         CIMD_MessageCenterClear(&CurrentSMSMessage->MessageCenter);     /* default */
1080         CurrentSMSMessage->MessageCenter.No=0;                          /* default - input for GetSMSCenter */
1081
1082         CurrentSMSMessage->Sender[0]='\0';                              /* default */
1083         CurrentSMSMessage->Destination[0]='\0';                         /* default */
1084         CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location;   /* default */
1085         /* CurrentSMSMessage->MemoryType is input argument */
1086         CurrentSMSMessage->Type=GST_UN;                                 /* default, detection of EMPTY SMSes! */
1087         CurrentSMSMessage->Status=GSS_SENTREAD;                         /* default */
1088         CurrentSMSMessage->Class=DEFAULT_CLASS;                         /* default */
1089         CurrentSMSMessage->EightBit=false;                              /* default */
1090         CurrentSMSMessage->Compression=false;                           /* default */
1091         /* CurrentSMSMessage->Location is input argument */
1092         CurrentSMSMessage->ReplyViaSameSMSC=false;                      /* default */
1093
1094         CIMD_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&CIMD_RX_Patrol_RetrieveReply_struct/*patrol*/,
1095                 CIMD_Cmd_Retrieve,
1096                         CIMD_Param_Retrieve_NULL);
1097         if (GE_NONE!=wait_on(&CurrentSMSMessageError,90/*timeout*/)) {
1098                 if (CIMD_RX_Patrol_Nak_Error!=CIMD_NAK_NO_SMS)
1099                         return(CurrentSMSMessageError);
1100                 /* We don't return GE_EMPTYSMSLOCATION as when we have already eaten
1101                  * all the wating SMSes there cannot be any other one
1102                  */
1103                 return(GE_INVALIDSMSLOCATION);
1104                 }
1105
1106         return(GE_NONE);
1107 }
1108
1109 static GSM_Error CIMD_SendSMS(GSM_Data *data, GSM_Statemachine *state)
1110 {
1111 u8 bintext[sizeof(CurrentSMSMessage->UDH)+sizeof(CurrentSMSMessage->MessageText)],*d;
1112 char hexbintext[sizeof(bintext) +1/*Trailing '\0'*/];
1113
1114         CurrentSMSMessage = data->SMSMessage;
1115
1116         CurrentSMSMessage->MessageNumber=0;     /* default */
1117
1118         d=bintext;
1119         if (CurrentSMSMessage->UDHPresent) {
1120 size_t UDHlen=1+CurrentSMSMessage->UDH[0];
1121
1122                 memcpy(d,CurrentSMSMessage->UDH,UDHlen);
1123                 d+=UDHlen;
1124 #ifdef CIMD_SUBMIT_7BIT
1125                 if (!CurrentSMSMessage->EightBit)       /* We are not able to send UDH by 7bit channel */
1126                         return(GE_NOTSUPPORTED);
1127 #endif
1128                 }
1129         if (
1130 #ifdef CIMD_SUBMIT_7BIT
1131                         1       /* Never do 7bit->8bit encoding when CIMD_SUBMIT_7BIT */
1132 #else
1133                         CurrentSMSMessage->EightBit
1134 #endif
1135                         ) {
1136                 memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
1137                 d+=CurrentSMSMessage->MessageTextLength;
1138         }
1139 #ifndef CIMD_SUBMIT_7BIT
1140         else {
1141 size_t byteslen=PackSevenBitsToEight(
1142                         /* check it out yourself, really the number of used bits for UDH header on the start
1143                          * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
1144                          */
1145                         (7-(d-bintext))%7,
1146                         CurrentSMSMessage->MessageText,d);
1147                 d+=byteslen;
1148         }
1149 #endif
1150
1151         if (
1152 #ifdef CIMD_SUBMIT_7BIT
1153                         CurrentSMSMessage->EightBit
1154 #else
1155                         1       /* Never use plain Submit for 8bit messages */
1156 #endif
1157                         ) {
1158                 *(SMS_BlockToHex(hexbintext,bintext,(d-bintext)/*len*/))='\0';
1159
1160                 /* DANGER: Keep in sync with CIMD_Cmd_Submit !!!
1161                  */
1162                 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1163                         CIMD_Cmd_SubmitBin,
1164                                 CIMD_Param_SubmitBin_Destination,CurrentSMSMessage->Destination,
1165                                 CIMD_Param_SubmitBin_Text,hexbintext,
1166                                 CIMD_Param_SubmitBin_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1167                                 CIMD_Param_SubmitBin_AUX,               /* Empty */
1168                                 CIMD_Param_SubmitBin_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1169                                 CIMD_Param_SubmitBin_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1170                                 CIMD_Param_SubmitBin_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1171                                 CIMD_Param_SubmitBin_NULL);
1172                 }
1173 #ifdef CIMD_SUBMIT_7BIT
1174         else {
1175                 *d='\0';
1176
1177                 /* DANGER: Keep in sync with CIMD_Cmd_SubmitBin !!!
1178                  */
1179                 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1180                         CIMD_Cmd_Submit,
1181                                 CIMD_Param_Submit_Destination,CurrentSMSMessage->Destination,
1182                                 CIMD_Param_Submit_Text,bintext,
1183                                 CIMD_Param_Submit_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1184                                 CIMD_Param_Submit_AUX,          /* Empty */
1185                                 CIMD_Param_Submit_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1186                                 CIMD_Param_Submit_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1187                                 CIMD_Param_Submit_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1188                                 CIMD_Param_Submit_NULL);
1189                 }
1190 #endif
1191
1192         return(GE_SMSSENDOK);
1193 }
1194
1195 static GSM_Error CIMD_Reset(GSM_Data *data, GSM_Statemachine *state)
1196 {
1197         CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
1198
1199         return(GE_NONE);
1200 }
1201
1202 #ifndef WIN32
1203
1204 /* Called by initialisation code to open comm port in asynchronous mode. */
1205
1206 static bool CIMD_OpenSerial(void)
1207 {
1208         int result;
1209   
1210 #if __unices__
1211         int rtn;
1212 #else
1213         struct sigaction sig_io;
1214
1215         /* Set up and install handler before enabling async IO on port. */
1216
1217         sig_io.sa_handler = CIMD_SigHandler;
1218         sig_io.sa_flags = 0;
1219         sigaction (SIGIO, &sig_io, NULL);
1220 #endif
1221
1222         /* Open device. */
1223
1224         result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, GCT_Serial);
1225
1226         if (!result) {
1227                 perror(_("Couldn't open CIMD device"));
1228                 return false;
1229         }
1230
1231 #if __unices__
1232         /* create a thread to handle incoming data from mobile phone */
1233         rtn = pthread_create(&selThread, NULL, (void*)CIMD_SelectLoop, (void*)NULL);
1234         if (rtn != 0) return false;
1235 #endif
1236
1237         /* device_changespeed() not needed as device_open() now automatically
1238          * sets the user-specified (or default) speed.
1239          */
1240         return (true);
1241 }
1242
1243 static void CIMD_SigHandler(int status)
1244 {
1245         unsigned char buffer[255];
1246         int count, res;
1247         res = device_read(buffer, 255);
1248         for (count = 0; count < res ; count ++)
1249                 CIMD_RX_Char(buffer[count]);
1250 }
1251 #endif /* WIN32 */
1252
1253 static bool CIMD_RX_Patrol_Ack(void)
1254 {
1255         if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i) { /* ==0x99 */
1256                 /* We cannot send the keepalive here as some dangerous operation may be in progress
1257                  */
1258                 CIMD_KeepAlivesLockInit();
1259                 pthread_mutex_lock(&CIMD_KeepAlivesLock);
1260                 CIMD_KeepAlives++;
1261                 pthread_mutex_unlock(&CIMD_KeepAlivesLock);
1262                 return(false);
1263                 }
1264
1265         if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1266                 return(false);          /* Ack for some unknown command */
1267
1268         free(CIMD_RX_Patrol_Ack_Error);
1269         CIMD_RX_Patrol_Ack_Error=strdup(CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Error)->u.StringZ.s);
1270
1271         /* Some patrol may have already indicated some error!
1272          */
1273          if (CIMD_CatchBufferErrorP && *CIMD_CatchBufferErrorP==GE_BUSY)
1274                  *CIMD_CatchBufferErrorP=GE_NONE;
1275
1276         return(true);
1277 }
1278
1279 static void CIMD_RX_Patrol_Ack_reset(void)
1280 {
1281         free(CIMD_RX_Patrol_Ack_Error);
1282         CIMD_RX_Patrol_Ack_Error=NULL;
1283 }
1284
1285 static const char *CIMD_RX_Patrol_Nak_resolve(u16 errorcode)
1286 {
1287 const struct CIMD_Param_Ack_Nak_Error *errorp;
1288
1289         for (errorp=CIMD_Param_Ack_Nak_Error;errorp<CIMD_Param_Ack_Nak_Error+ARRAY_LEN(CIMD_Param_Ack_Nak_Error);errorp++)
1290                 if (errorcode==errorp->code)
1291                         return(_(errorp->msg));
1292         return(_("Unknown error code"));
1293 }
1294
1295 static void CIMD_RX_Patrol_Nak_dump(u16 errorcode)
1296 {
1297         fprintf(stderr,_("Got CIMD error code 0x%04X: "),errorcode);
1298         if (!(errorcode & ~0x000F)) {   /* combined error code */
1299 u16 errormask;
1300                 fprintf(stderr,_("combined:"));
1301                 for (errormask=0x0001;errormask<=errorcode;errormask<<=1)
1302                         if (errorcode&errormask)
1303                                 fprintf(stderr," +%s",CIMD_RX_Patrol_Nak_resolve(errormask));
1304                 fputc('\n',stderr);
1305                 return;
1306                 }
1307         fprintf(stderr,"%s\n",CIMD_RX_Patrol_Nak_resolve(errorcode));
1308 }
1309
1310 static bool CIMD_RX_Patrol_Nak(void)
1311 {
1312         if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i        /* ==0x99 */
1313                 && CIMD_NAK_RESEND == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i) {    /* 0x9999 */
1314                 fprintf(stderr,_("WARNING: Requested to resend last packet!!\n"));
1315                 CIMD_TX_SendCommandResend();
1316                 return(true);
1317                 }
1318
1319         if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1320                 return(false);          /* Nak for some unknown command */
1321
1322         CIMD_RX_Patrol_Nak_Error=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i;
1323         if (CIMD_CatchBufferErrorP)
1324                 *CIMD_CatchBufferErrorP=GE_INTERNALERROR;
1325
1326         CIMD_RX_Patrol_Nak_dump(CIMD_RX_Patrol_Nak_Error);
1327
1328         return(true);
1329 }
1330
1331 static void CIMD_RX_Patrol_Nak_reset(void)
1332 {
1333         CIMD_RX_Patrol_Nak_Error=0;
1334 }
1335
1336 static const struct CIMD_RX_Patrol CIMD_RX_Patrols[]={
1337         { CIMD_Cmd_Ack,CIMD_RX_Patrol_Ack,CIMD_RX_Patrol_Ack_reset },
1338         { CIMD_Cmd_Nak,CIMD_RX_Patrol_Nak,CIMD_RX_Patrol_Nak_reset },
1339         };
1340
1341
1342 static void CIMD_CatchBufferReset(void)
1343 {
1344         CIMD_CatchBufferPtr=CIMD_CatchBuffer;
1345 }
1346
1347 static void CIMD_CatchBufferStart(GSM_Error *errorcodep)
1348 {
1349 static GSM_Error err_trashcan;
1350 const struct CIMD_RX_Patrol *patrol;
1351
1352         if (!errorcodep)
1353                 errorcodep=&err_trashcan;
1354         CIMD_CatchBufferErrorP=errorcodep;
1355
1356         CIMD_CatchBufferReset();
1357
1358         for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1359                 if (patrol->reset)
1360                         (*patrol->reset)();
1361 }
1362
1363 static u8 *CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1364
1365 /* RETURNS: Success
1366  */
1367 static bool CIMD_RX_ProcessRawPacket_parseparam(struct CIMD_paramslot *slot)
1368 {
1369         switch (slot->elf) {
1370
1371                 case CIMD_ELF_HexByte: {
1372 unsigned parsedbyte;
1373                         if (0   || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1374                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1375                                 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[2]
1376                                 )
1377                                 return(false);
1378                         if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedbyte))
1379                                 return(false);
1380                         slot->u.HexByte.i=parsedbyte;
1381                         CIMD_RX_ProcessRawPacket_parseparam_buf_s+=2+1;
1382                         } break;
1383
1384                 case CIMD_ELF_HexWord: {
1385 unsigned parsedword;
1386                         if (0   || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1387                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1388                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[2])
1389                                 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[3])
1390                                 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[4]
1391                                 )
1392                                 return(false);
1393                         if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedword))
1394                                 return(false);
1395                         slot->u.HexWord.i=parsedword;
1396                         CIMD_RX_ProcessRawPacket_parseparam_buf_s+=4+1;
1397                         } break;
1398
1399                 case CIMD_ELF_Decimal: {
1400 int parsedint;
1401 u8 *start=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1402                         if (CIMD_MARK_SEP==*start)
1403                                 return(false);          /* empty Decimal not permitted */
1404                         while (isdigit(*CIMD_RX_ProcessRawPacket_parseparam_buf_s))
1405                                 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1406                         if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++)        /* skip CIMD_MARK_SEP */
1407                                 return(false);
1408                         if (1!=sscanf(start,"%d" CIMD_MARK_SEPS,&parsedint))
1409                                 return(false);
1410                         slot->u.Decimal.i=parsedint;
1411                         } break;
1412
1413                 case CIMD_ELF_StringZ:
1414                         slot->u.StringZ.s=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1415                         while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1416                                 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1417                         *CIMD_RX_ProcessRawPacket_parseparam_buf_s++='\0';      /* skip CIMD_MARK_SEP */
1418                         break;
1419
1420                 case CIMD_ELF_HexBlock:
1421                         slot->u.HexBlock.buf=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1422
1423                         while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1424                                 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1425                         if ((CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf)&1)
1426                                 return(false);          /* odd number of xdigits */
1427
1428                         slot->u.HexBlock.len=(CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf);
1429                         if (!SMS_BlockFromHex(slot->u.HexBlock.buf/*d*/,slot->u.HexBlock.buf/*s*/,slot->u.HexBlock.len*2))
1430                                 return(false);          /* parse error */
1431
1432                         CIMD_RX_ProcessRawPacket_parseparam_buf_s++;    /* skip CIMD_MARK_SEP */
1433                         break;
1434
1435                 case CIMD_ELF_Empty:
1436                         if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1437                                 return(false);          /* some content found */
1438                         break;
1439                 }
1440         return(true);   /* success */
1441 }
1442
1443
1444 static bool CIMD_RX_EvalPatrol(const struct CIMD_RX_Patrol *patrol)
1445 {
1446         if (!patrol)
1447                 return(false);  /* not recognized */
1448         if (CIMD_RX_Packet_Cmd!=patrol->cmd)
1449                 return(false);  /* not recognized */
1450         return((*patrol->func)());
1451 }
1452
1453 static inline void CIMD_RX_ProcessPacket(void)
1454 {
1455 const struct CIMD_RX_Patrol *patrol;
1456
1457         for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1458                 if (CIMD_RX_EvalPatrol(patrol))
1459                         return;         /* recognized and processed - whether successfuly is not interesting */
1460         CIMD_RX_EvalPatrol(CIMD_RX_Patrol_Current);
1461 }
1462
1463 static inline void CIMD_RX_ProcessRawPacket(void)
1464 {
1465 unsigned xsumgot;
1466 u8 *u8s,xsum;
1467 u16 position;
1468 const struct CIMD_Param *param;
1469 const struct CIMD_Cmd *cmdp;
1470
1471 #ifdef CIMD_DEBUG
1472         write(1,"GOT:<STX>",9);
1473         write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1));
1474         write(1,"<ETX>\n",6);
1475 #endif
1476
1477         if (CIMD_CatchBufferPtr<CIMD_CatchBuffer +1/*START*/ +1/*SEP*/ +2/*xsum*/ +1/*STOP*/)
1478                 return;         /* error - buffer too short */
1479         if (0   || CIMD_CatchBufferPtr[-4]!=CIMD_MARK_SEP
1480                 || !isxdigit(CIMD_CatchBufferPtr[-3])
1481                 || !isxdigit(CIMD_CatchBufferPtr[-2])
1482                 )
1483                 return;         /* error - invalid buffer tail */
1484
1485         /* (*Ptr) will now point to the end of parameter part */
1486         CIMD_CatchBufferPtr-=3;
1487         if (1!=sscanf(CIMD_CatchBufferPtr,"%X" CIMD_MARK_SEPS,&xsumgot))
1488                 return;         /* INTERNAL - we have already checked the validity! */
1489         xsum=0;
1490         for (u8s=CIMD_CatchBuffer;u8s<CIMD_CatchBufferPtr;u8s++)
1491                 xsum+=*u8s;
1492         if (xsum!=xsumgot)
1493                 return;         /* error - invalid checksum */
1494
1495         CIMD_RX_ProcessRawPacket_parseparam_buf_s=CIMD_CatchBuffer +1/*START*/;
1496         CIMD_RX_Packet_slots=1;         /* just [0] now */
1497         CIMD_RX_Packet_slot[0].code=0;  /* ignored now - position */
1498         CIMD_RX_Packet_slot[0].elf=CIMD_ELF_HexByte;
1499         if (!CIMD_RX_ProcessRawPacket_parseparam(CIMD_RX_Packet_slot+0))
1500                 return;         /* error - cmd code not parsed */
1501
1502         for (cmdp=CIMD_Cmd;cmdp<CIMD_Cmd+CIMD_Cmdcnt;cmdp++)
1503                 if (cmdp->code==CIMD_RX_Packet_slot[0].u.HexByte.i) break;
1504         if (cmdp>=CIMD_Cmd+CIMD_Cmdcnt)
1505                 return;         /* error - unknown command */
1506         CIMD_RX_Packet_Cmd=(enum CIMD_Cmd_Type)(cmdp-CIMD_Cmd);
1507
1508         /* "position" handling is BIP dependency! */
1509         for (position=1;CIMD_RX_ProcessRawPacket_parseparam_buf_s<CIMD_CatchBufferPtr;position++) {
1510 struct CIMD_paramslot *slot;
1511
1512                 /* This code search is a crude overhead just for BIP now :-)
1513                  */
1514                 for (param=cmdp->param;param<cmdp->param+cmdp->paramcnt;param++)
1515                         if (position==param->code)
1516                                 break;
1517                 if (param>=cmdp->param+cmdp->paramcnt) {        /* not found - ignore */
1518                         while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++);    /* skip CIMD_MARK_SEP */
1519                         continue;
1520                         }
1521                 slot=CIMD_RX_Packet_slot+1+(param-cmdp->param);
1522                 slot->code=param->code;
1523                 slot->elf=param->elf;
1524                 if (!CIMD_RX_ProcessRawPacket_parseparam(slot))
1525                         return;         /* error - unable to parse required param */
1526                 /* CIMD_MARK_SEP is skipped by CIMD_RX_ProcessRawPacket_parseparam() automatically
1527                  */
1528                 CIMD_RX_Packet_slots++;
1529                 }
1530         /* The packet is now parsed */
1531
1532         CIMD_RX_ProcessPacket();
1533 }
1534
1535 /* RX_State machine for receive handling.  Called once for each character
1536    received from the phone/phone. */
1537
1538 static void CIMD_RX_Char(char rx_byte)
1539 {
1540 #ifdef CIMD_DEBUG
1541 #if 0
1542         dprintf(_("Received character '%c' (0x%02X)\n"),
1543                 (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
1544 #endif
1545         putchar(rx_byte);
1546 #endif
1547
1548         /* NEVER store '\0' as it would knock-out completely our patrolling system!
1549          * It is invalid anyway
1550          */
1551         if (rx_byte=='\0') {
1552                 CIMD_CatchBufferReset();
1553                 return;
1554                 }
1555
1556         if (CIMD_CatchBufferPtr==CIMD_CatchBuffer && rx_byte!=CIMD_MARK_START) {
1557                 /* No haven't yet catched any START yet */
1558                 return;
1559                 }
1560         if (CIMD_CatchBufferPtr>=CIMD_CatchBuffer+sizeof(CIMD_CatchBuffer)) {
1561                 fprintf(stderr,_("WARNING: Incoming CIMD packet too long to fit (>%d bytes)!\n"),sizeof(CIMD_CatchBuffer));
1562                 CIMD_CatchBufferReset();
1563                 }
1564
1565         *CIMD_CatchBufferPtr++=rx_byte;
1566         if (rx_byte==CIMD_MARK_STOP) {
1567                 CIMD_RX_ProcessRawPacket();
1568                 CIMD_CatchBufferReset();
1569                 }
1570 }
1571
1572 /* Here we initialise model specific functions. */
1573
1574 #define CIMD_FUNCTIONS_ENTRY(name) \
1575         case GOP_##name:        return(CIMD_##name(data,state));
1576
1577 static GSM_Error CIMD_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
1578 {
1579 static GSM_Statemachine *catcher=NULL;
1580
1581         if (!catcher || catcher==state)
1582                 catcher=state;
1583         else
1584                 *((char *)NULL)=1;
1585
1586         switch (op) {
1587                 CIMD_FUNCTIONS_ENTRY(Init)
1588                 CIMD_FUNCTIONS_ENTRY(Terminate)
1589                 CIMD_FUNCTIONS_ENTRY(GetSMSStatus)
1590                 CIMD_FUNCTIONS_ENTRY(GetSMS)
1591                 CIMD_FUNCTIONS_ENTRY(SendSMS)
1592                 CIMD_FUNCTIONS_ENTRY(GetImei)
1593                 CIMD_FUNCTIONS_ENTRY(GetRevision)
1594                 CIMD_FUNCTIONS_ENTRY(GetModel)
1595                 CIMD_FUNCTIONS_ENTRY(GetManufacturer)
1596                 CIMD_FUNCTIONS_ENTRY(Reset)
1597
1598                 case GOP_Identify: {
1599 GSM_Error err,r=GE_NONE;
1600                         if (GE_NONE!=(err=CIMD_GetImei        (data,state)))
1601                                 r=err;
1602                         if (GE_NONE!=(err=CIMD_GetRevision    (data,state)))
1603                                 r=err;
1604                         if (GE_NONE!=(err=CIMD_GetModel       (data,state)))
1605                                 r=err;
1606                         if (GE_NONE!=(err=CIMD_GetManufacturer(data,state)))
1607                                 r=err;
1608                         return(r);
1609                         }
1610
1611                 default:
1612                         return(GE_NOTIMPLEMENTED);
1613         }
1614 }
1615
1616 GSM_Phone phone_cimd = {
1617         UNIMPLEMENTED,  /* IncomingFunctions - we don't use default StateMachine */
1618         UNIMPLEMENTED,  /* DefaultFunction   - we don't use default StateMachine */
1619         /* Mobile phone information */
1620         {
1621                 "BIP" /* |CIMD - not yet */,    /* Supported models */
1622                 100,                    /* Max RF Level (AT+CSQ) */
1623                 0,                      /* Min RF Level (AT+CSQ) */
1624                 GRF_Percentage,         /* RF level units */
1625                 100,                    /* Max Battery Level (AT+CBC) */
1626                 0,                      /* Min Battery Level (AT+CBC) */
1627                 GBU_Percentage,         /* Battery level units */
1628                 GDT_None,               /* Have date/time support */
1629                 GDT_None,               /* Alarm supports time only */
1630                 0,                      /* No alarm available */
1631                 48, 84,                 /* Startup logo size */
1632                 14, 72,                 /* Op logo size */
1633                 14, 72,                 /* Caller logo size */
1634         },
1635         CIMD_Functions,
1636 };