This commit was generated by cvs2svn to compensate for changes in r164,
[gnokii.git] / common / at-hw.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 functions on AT (Hayes) command
15   compatible hardware modems such as Siemens M20.
16   See README-AT for more details on supported mobile phones and GSM modems.
17
18   The various routines are called ATHW (whatever) as a concatenation of AT
19   and HardWare communication type.
20
21   $Log$
22   Revision 1.1.1.1  2002/04/03 00:08:02  short
23   Found in "gnokii-working" directory, some November-patches version
24
25
26 */
27
28 #define ATHW_DEBUG 1
29 /* #define DISABLE_CMGF0 1 */           /* Force AT+CMGF=1 on phones capable +CMGF=0 */
30
31 /* System header files */
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <limits.h>
38  
39
40 #ifdef WIN32
41
42 #include <windows.h>
43 #include "win32/winserial.h"
44
45 #undef IN
46 #undef OUT
47
48 #define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
49 #define sleep(x) Sleep((x) * 1000)
50 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
51 extern HANDLE hPhone;
52
53 #else
54
55 #define WRITEPHONE(a, b, c) device_write(b, c)
56 #include <unistd.h>
57 #include <termios.h>
58 #include <fcntl.h>
59 #include <ctype.h>
60 #include <signal.h>
61 #include <sys/ioctl.h>
62 #include <sys/types.h>
63 #include <sys/time.h>
64 #include <pthread.h>
65 #include <errno.h>
66 #include "device.h"
67 #include "devices/unixserial.h"
68
69 #endif
70
71 /* Various header file */
72
73 #include "config.h"
74 #include "misc.h"
75 #include "gsm-common.h"
76
77 /* Global variables used by code in gsm-api.c to expose the functions
78    supported by this model of phone. */
79
80
81 #if __unices__
82 /* fd opened in device.c */
83 extern int device_portfd;
84 #endif
85
86 /* Our private defines */
87
88 #define ATHW_CME_NOT_FOUND            (22)      /* +CME ERROR: 22 */
89 #define ATHW_CMS_INVALID_MEMORY_INDEX (321)     /* +CMS ERROR: 321 */
90
91 /* When now catchbuffer was provided and must have some space to decode
92  * OK/ERROR/... result codes.
93  */
94 #define ATHW_CATCHBUFFER_LENGTH 0x400
95
96 /* Default message reference, filled-in by GSM modem
97  */
98 #define ATHW_PDU_MR_DEFAULT (0x00)
99
100 /* Maximum PDU size in bytes
101  */
102 #define GNOKII_MAX_PDU_LENGTH (64+GSM_MAX_DESTINATION_LENGTH/2+(GSM_MAX_SMS_LENGTH*7)/8)
103
104 /* Local variables */
105
106
107 #ifndef WIN32
108 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
109 #endif
110 static bool RequestTerminate;
111 static GSM_MemoryType ATHW_CurrentMemoryType=GMT_XX;
112 static int ATHW_CurrentCMGF=-1;
113 static bool ATHW_CMGS_CMGF1_8bit_HaveBinHex;    /* AT+CMGS in +CMGF==1 and 8bit mode accepts hexstring */
114 static bool ATHW_HaveCNMI=false;
115 static bool ATHW_HaveSiemensMGR=false;  /* "AT^SMGR" supported? */
116 static bool ATHW_HaveSiemensMGL=false;  /* "AT^SMGL" supported? */
117 static int ATHW_CNMI_count=-1;  /* value (-1) means it is not yet known */
118
119 /* We don't know whether we should place SMSC in the front of +CMGS PDU
120  * so we will try both methods.
121  * When we will at least once successfuly send a message, we will *_force-it
122  * as the probability of wrong settings and successful message send is REALLY low. :-)
123  * (Maybe not so low when we will try to send prefix and no prefix is expected,
124  * this is also the reason why we will first try to NOT to send the prefix as we
125  * would have to successfuly hit some valid SMS center by the initial part of PDU.)
126  * Note: Applicable only if +CMGF==0
127  */
128 static bool ATHW_CurrentSMSCPrefix=false;
129 static bool ATHW_CurrentSMSCPrefix_force=false;
130 /* Always try 4 retries - w/o prefix, w/prefix, w/o prefix again, w/prefix again, fail
131  * This number should be probable even to give some 'stability' when all SMSes are failing
132  */
133 #define ATHW_CURRENTSMSCPREFIX_RETRIES (4)
134
135 /* Please see the comment above ATHW_SaveSMS_StatSupported_solve().
136  * Number of retry sessions before giving up sending SMS <stat> during SaveSMS.
137  */
138 #define ATHW_SAVESMS_STATSUPPORTED_RETRIES (2)
139
140
141 static char ATHW_CatchBuffer[ATHW_CATCHBUFFER_LENGTH];
142 static char *ATHW_CatchBufferPtr=ATHW_CatchBuffer;      /* current destination writing ptr */
143 static char *ATHW_CatchBufferMarker;                    /* marks begin of currently catched stream */
144 static GSM_Error *ATHW_CatchBufferErrorP;
145
146 static long ATHW_RX_Patrol_CME_ERROR_code;
147 static long ATHW_RX_Patrol_CMS_ERROR_code;
148
149 typedef char *(*ATHW_RX_PatrolFunc)(char *after);
150 typedef void (*ATHW_RX_PatrolReset)(void);
151
152 struct ATHW_RX_Patrol {
153         const char *buoy;
154         ATHW_RX_PatrolFunc func;
155         ATHW_RX_PatrolReset reset;
156         };
157
158 static const struct ATHW_RX_Patrol *ATHW_RX_Patrol_Current;
159
160 static void ATHW_CatchBufferReset(void);
161 static void ATHW_CatchBufferMarkStart(GSM_Error *errorcodep);
162
163 #ifndef WIN32
164
165 static pthread_t Thread;
166 # if __unices__
167 static pthread_t selThread;
168 # endif
169
170 #endif
171
172 /* Local variables used by get/set phonebook entry code. Buffer is used as a
173    source or destination for phonebook data and other functions... Error is
174    set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
175    handler routines as appropriate. */
176
177 static GSM_MemoryStatus   *CurrentMemoryStatus;
178 static GSM_Error          CurrentMemoryStatusError;
179
180 static GSM_PhonebookEntry *CurrentPhonebookEntry;
181 static GSM_Error          CurrentPhonebookError;
182
183 static GSM_SMSMessage     *CurrentSMSMessage;
184 static char               *CurrentSMSMessagePDU;        /* hex string representation for +CMGF=0 patrol */
185 static size_t             CurrentSMSMessagePDU_size;    /* used only if (+CMGF==0), size in bytes */
186 static GSM_Error          CurrentSMSMessageError;
187
188 static GSM_SMSStatus      *CurrentSMSStatus;
189 static GSM_Error          CurrentSMSStatusError;
190
191 static GSM_MessageCenter  *CurrentMessageCenter;
192 static GSM_Error          CurrentMessageCenterError;
193
194 static float              *CurrentRFLevel;              /* AT+CSQ */
195 static GSM_Error          GetRFLevelError;
196
197 static float              *CurrentBatteryLevel;         /* AT+CBC */
198 static GSM_Error          GetBatteryLevelError;
199
200 static GSM_PowerSource    *CurrentPowersource;          /* AT+CBC */
201 static GSM_Error          GetPowersourceError;
202
203 static GSM_Error          DialVoiceError;
204
205 static GSM_Error          CancelCallError;
206
207 static GSM_Error          CurrentPhoneInfoError;
208
209 static unsigned char      Manufacturer[GSM_MAX_MANUFACTURER_LENGTH];
210 static unsigned char      Model[GSM_MAX_MODEL_LENGTH];
211 static unsigned char      Revision[GSM_MAX_REVISION_LENGTH];
212 static unsigned char      IMEI[GSM_MAX_IMEI_LENGTH];
213
214 static char               CurrentIncomingCall[20] = " ";
215
216 static GSM_NetworkInfo    *CurrentNetworkInfo;
217 static GSM_Error          CurrentNetworkInfoError;
218
219
220 /* Pointer to a callback function used to return changes to a calls status */
221 /* This saves unreliable polling */
222 static void (*CallPassup)(char c);
223
224
225 /* "catchbufer" can be provided as NULL:
226  *  - sizeof() will be bogus but it will be ingored anyway
227  */
228
229 static void ATHW_TX_SendCommand(GSM_Error *errorcodep,const struct ATHW_RX_Patrol *patrol,const char *fmt,...) G_GNUC_PRINTF(3,4);
230 static void ATHW_TX_SendCommand(GSM_Error *errorcodep,const struct ATHW_RX_Patrol *patrol,const char *fmt,...)
231 {
232 char *command;
233 size_t command_len;
234 va_list ap;
235 int writephone_got;
236
237         if (errorcodep)
238                 *errorcodep=GE_BUSY;
239         ATHW_RX_Patrol_Current=patrol;
240
241         va_start(ap,fmt);
242         command_len=gvasprintf(&command,fmt,ap);
243         va_end(ap);
244         if (-1==command_len) {
245                 if (errorcodep)
246                         *errorcodep=GE_INTERNALERROR;
247                 return;
248                 }
249
250         ATHW_CatchBufferMarkStart(errorcodep);
251
252         writephone_got=WRITEPHONE(PortFD, command, command_len);
253
254 #ifdef ATHW_DEBUG
255         write(1,"CMD:",4);
256         write(1,command,command_len);
257         write(1,"\n",1);
258 #endif
259
260         free(command);
261         if (command_len!=writephone_got) {
262                 if (errorcodep)
263                         *errorcodep=GE_INTERNALERROR;
264                 return;
265                 }
266
267         /* success but we don't wait for the result code */
268 }
269
270 /* This function is used to get storage status from the phone. It currently
271    supports two different memory areas - internal and SIM. */
272
273 static GSM_Error 
274 wait_on(volatile GSM_Error *what, int timeout)
275 {
276 GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
277
278         while (timeout && ((r=*what)==GE_BUSY)) {
279                 if (!--timeout) {
280                         r=GE_TIMEOUT;
281                         break;
282                         }
283                 usleep(100000);
284         }
285         /* any specific patrollers are no longer valid */
286         ATHW_RX_Patrol_Current=NULL;
287
288 #ifdef ATHW_DEBUG
289         printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
290 #endif
291
292         return(r);
293 }
294
295 #define WAIT_ON(what, timeout) \
296         do { \
297                 GSM_Error res = wait_on(what, timeout); \
298                 if (res != GE_NONE) \
299                         return res; \
300         } while (0)
301
302 /* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
303  */
304 #define ATHW_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
305         do { \
306 GSM_Error _ATHW_TX_SENDCOMMAND_WAIT_ON_err,*_ATHW_TX_SENDCOMMAND_WAIT_ON_errp; \
307  \
308                 if (!(_ATHW_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
309                         _ATHW_TX_SENDCOMMAND_WAIT_ON_errp=&_ATHW_TX_SENDCOMMAND_WAIT_ON_err; \
310                 ATHW_TX_SendCommand(_ATHW_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
311                 WAIT_ON(_ATHW_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
312         } while (0)
313
314
315 #define ATHW_ERR_WRAPPER(expr) \
316         do { \
317                 GSM_Error err=(expr); \
318                 if (err!=GE_NONE) \
319                         return(err); \
320         } while (0)
321
322 /* Currently we use a heuristic for extraction as at least Nokia 9000i doesn't
323  * escape quote characters even if they are inside enquoted strings!
324  * When we find -- ", -- we assume it is the separator although it may be false!
325  * Hmm, Siemens M20 has the same broken behaviour.
326  */
327 static void ATHW_ExtractString(char *dest,size_t destlen,char *src,int element_no)
328 {
329 char *srcend,*start,*end;
330
331         *dest='\0';
332         if (!(srcend=strchr(src,'\n')))
333                 return;         /* INTERNAL error! */
334         do {
335                 while (*src==' ') src++;
336                 if (*src!='"') {
337                         start=src;
338                         while (src<srcend && *src!=',') src++;
339                         end=src;
340                         }
341                 else {          /* *src=='"' */
342                         start=++src;
343                         while (src<srcend && !(src[0]=='"' && (src[1]==',' || src+1==srcend))) src++;
344                         end=src;
345                         if (*src=='"')
346                                 src++;
347                         }
348                 while (*src==' ') src++;
349                 if (*src==',')
350                         src++;
351                 /* here we have <start..end) as the current element */
352                 } while (element_no--);
353
354         if (destlen-1<end-start)
355                 end=start+destlen-1;
356
357         memcpy(dest,start,end-start);
358         dest[end-start]='\0';
359 }
360
361 #define ATHW_EXTRACTSTRING(dest,src,element_no) (ATHW_ExtractString((dest),sizeof((dest)),(src),(element_no)))
362
363 static long ATHW_ExtractNumber(char *src,int element_no)
364 {
365 char buf[32],*end=NULL;
366 long r;
367
368         ATHW_EXTRACTSTRING(buf,src,element_no);
369         r=strtol(buf,&end,10);
370         if (!*buf || (end && *end))
371                 return(LONG_MIN);
372         return(r);
373 }
374
375 /* s/^\s+//; s/\s+$//; s/\s+/ /g; */
376
377 static void ATHW_BufferTrimCopy(char *d_buf,size_t d_len,const char *s)
378 {
379 char *d_end=d_buf+d_len,*d;
380
381         for (d=d_buf;*s && d<d_end -1/*Terminating '\0'*/ ;s++) {
382                 if (isspace(*s)) {
383                         if (d==d_buf || d[-1]==' ')
384                                 continue;
385                         *d++=' ';
386                         continue;
387                         }
388                 *d++=*s;
389                 }
390         if (d>d_buf && d[-1]==' ')
391                 d--;
392         *d='\0';
393 }
394
395 #define ATHW_BUFFERTRIMCOPY_OFFSET(d,s,offset) (ATHW_BufferTrimCopy((d)+(offset),sizeof((d))-(offset),(s)))
396 #define ATHW_BUFFERTRIMCOPY(d,s) (ATHW_BUFFERTRIMCOPY_OFFSET((d),(s),0))
397
398 static GSM_Error ATHW_GetPhoneInfo(void)
399 {
400 /* +CGMI=Request Manufacturer Identification
401  * +CGMM=Request Model Identification
402  * +CGMR=Request Revision Identification
403  * +CGSN=Request Product Serial Number Identification (IMEI)
404  */
405
406         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMI\r");
407         ATHW_BUFFERTRIMCOPY(Manufacturer,ATHW_CatchBufferMarker);
408
409         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMM\r");
410         ATHW_BUFFERTRIMCOPY_OFFSET(Model,ATHW_CatchBufferMarker,3);
411         memcpy(Model,"AT-",3);
412
413         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMR\r");
414         ATHW_BUFFERTRIMCOPY(Revision,ATHW_CatchBufferMarker);
415
416         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGSN\r");
417         ATHW_BUFFERTRIMCOPY(IMEI,ATHW_CatchBufferMarker);
418
419         return(GE_NONE);
420 }
421
422 static unsigned char ATHW_SMStoFO(GSM_SMSMessage *SMS)
423 {
424         return(0
425                         |((SMS->Type==GST_MO)<<0)
426                         |(2<<3) /*<vp>=integer*/
427                         |0 /* bit 5 is Status Report Request, not supported */
428                         |((!!SMS->UDHPresent)<<6) /*UDH set*/
429                         |((!!SMS->ReplyViaSameSMSC)<<7) /*ReplyPath*/
430                         );
431 }
432
433 static unsigned char ATHW_SMStoDCS(GSM_SMSMessage *SMS)
434 {
435 int class=(SMS->Class==-1 ? 1 : SMS->Class);    /* we default to class 1 (Mobile Equipment target) */
436
437         if (!SMS->EightBit && class==1)
438                 return(0x00);
439         return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
440 }
441
442 /* Nokia 9000i: We need to temporarily turn on ECHO otherwise we wouldn't got the "\n> " prompt
443  * (At least Siemens M20 doesn't have this broken behaviour.)
444  */
445 static GSM_Error ATHW_SMS_CMGF01_pre(void)
446 {
447         if (ATHW_CurrentCMGF==1) {
448                 /* We just cannot send UDH header in pure CMGF 1 mode */
449                 if (CurrentSMSMessage->UDHPresent && (!CurrentSMSMessage->EightBit || !ATHW_CMGS_CMGF1_8bit_HaveBinHex))
450                         return(GE_INTERNALERROR);
451
452                 /* AT+CSMP=<fo>,<vp>,<pid>,<dcs> */
453                 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
454                         "AT+CSMP=%d,%d,%d,%d\r",
455                         /*  <fo>: */ ATHW_SMStoFO(CurrentSMSMessage),
456                         /*  <vp>: */ SMS_Validity_to_VP(CurrentSMSMessage->Validity),
457
458                         /* FIXME: Should we query current "MessageCenter.No" to get "Format" field? */
459                         /* <pid>: */ (unsigned char)CurrentSMSMessage->MessageCenter.Format,
460
461                         /* <dcs>: */ ATHW_SMStoDCS(CurrentSMSMessage)
462                         );
463                 }
464
465         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
466                 "ATE1\r");
467
468         return(GE_NONE);
469 }
470
471 static GSM_Error ATHW_SMS_CMGF01_post(void)
472 {
473         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
474                 "ATE0\r");
475
476         return(GE_NONE);
477 }
478
479 /* For now we just kick out all quotes in the source "string" as no escaping
480  * method was found on Nokia 9000i - what the other ones?
481  */
482 static const char *ATHW_Enquote(const char *string)
483 {
484
485 #define ATHW_ENQUOTE_SLOTS (4)
486
487 struct ATHW_Enquote_slot {
488         char *buf;
489         size_t len;
490         };
491
492 static struct ATHW_Enquote_slot slots[ATHW_ENQUOTE_SLOTS];
493 static struct ATHW_Enquote_slot *slot=slots;
494 size_t stringl=strlen(string);
495 char *d;
496 const char *r,*s;
497
498         if (slot->len<1+stringl+1+1) {  /* "string"\0 */
499 char *newbuf;
500 size_t newlen=(!slot->len ? 0x100 : slot->len*2);
501
502                 if (!(newbuf=realloc(slot->buf,newlen)))
503                         return(NULL);
504                 slot->buf=newbuf;
505                 slot->len=newlen;
506                 }
507
508         d=slot->buf;
509         *d++='"';
510         for (s=string;*s;s++) {
511                 if (*s=='"')
512                         continue;
513                 *d++=*s;
514                 }
515         *d++='"';
516         *d++='\0';
517
518         r=slot->buf;
519         if (++slot>=slots+ARRAY_LEN(slots))
520                 slot=slots;
521         return(r);
522 }
523
524 static char *ATHW_PhoneSetup_CMGF1_Detect_Patrol_9(char *after)
525 {
526         /* We got at least echo of the initial "9" digit.
527          * Nokia 9000i/9110i/CardPhone will echo even "Q" followed by ERROR,
528          * but Nokia 9210 will give ERROR immediately (w/o echo of "Q").
529          * When the feature is not supported we will get GE_TIMEOUT and 
530          * ATHW_CMGS_CMGF1_8bit_HaveBinHex will be reset to "false" by the error check
531          * in ATHW_PhoneSetup_CMGF1_Detect_core().
532          */
533         ATHW_CMGS_CMGF1_8bit_HaveBinHex=true;
534
535         WRITEPHONE(PortFD,"Q",1);
536
537         /* We don't need to patrol for "Q", its detection would be cross-phone
538          * incompatible.
539          */
540
541         return(after);  /* eat line - it will be just one character... */
542 }
543
544 static const struct ATHW_RX_Patrol ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct=
545         { "9",ATHW_PhoneSetup_CMGF1_Detect_Patrol_9 };
546
547 static char *ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE(char *after)
548 {
549         WRITEPHONE(PortFD,"9",1);
550         ATHW_RX_Patrol_Current=&ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct;
551         return(after);  /* eat line - it will be just one character... */
552 }
553
554 static const struct ATHW_RX_Patrol ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct=
555         { "\n> ",ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE };
556
557 /* ATHW_CMGS_CMGF1_8bit_HaveBinHex feature is AFAIK present on Nokia 9000i/9110
558  * and also on Nokia CardPhone (but CardPhone supports +CMGF==0 so it is useless there).
559  * On Nokia 9000i we will detect the feature as present although it cannot be used
560  * for logo/ring sending as it will later fail to set UDH bit in FO by AT+CSMP.
561  */
562 static GSM_Error ATHW_PhoneSetup_CMGF1_Detect_core(void)
563 {
564 GSM_SMSMessage sms;
565
566         ATHW_CMGS_CMGF1_8bit_HaveBinHex=false;  /* default */
567
568         /* We need to reset the catchbuffer as we would otherwise
569          * catch some completely bogus previous output.
570          * We are called only during phone setup so here is no risk in loosing
571          * some unsolicited result codes.
572          */
573         ATHW_CatchBufferReset();
574
575         CurrentSMSMessage=&sms;
576         /* for ATHW_SMStoFO(): */
577         CurrentSMSMessage->UDHPresent=false;
578         CurrentSMSMessage->Type=GST_MO;
579         CurrentSMSMessage->UDHPresent=false;            /* warning: it would fail on Nokia 9000i or Siemens M20! */
580         CurrentSMSMessage->ReplyViaSameSMSC=false;
581         /* for SMS_Validity_to_VP(): */
582         CurrentSMSMessage->Validity=GSMV_72_Hours;      /* default */
583         /* for ATHW_SMS_CMGF01_pre(): */
584         CurrentSMSMessage->MessageCenter.Format=GSMF_Text;
585         /* for ATHW_SMStoDCS(): */
586         CurrentSMSMessage->EightBit=true;               /* IMPORTANT!: The ONLY meaning of the whole procedure! */
587         CurrentSMSMessage->Class=1;
588         /* Do proper AT+CSMP= and also ATE1 */
589         ATHW_SMS_CMGF01_pre();
590
591         ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct/*patrol*/,
592                 "AT+CMGS=%s\r",ATHW_Enquote("123456"/*just some test number*/));
593
594         /* Timeout 5 is too small for Nokia 9000i, give it a rest...
595          */
596         if (GE_TIMEOUT==wait_on(&CurrentSMSMessageError,20/*timeout*/))
597                 ATHW_CMGS_CMGF1_8bit_HaveBinHex=false;
598
599         return(GE_NONE);
600 }
601
602 static GSM_Error ATHW_PhoneSetup_CMGS_Reset(void)
603 {
604 GSM_Error err;
605 int retry;
606
607         /* We try to escape AT+CMGS mode, at least Siemens M20 then needs to get some rest
608          */
609         WRITEPHONE(PortFD,"\x1B\r",2);
610         usleep(500000);
611
612         ATHW_CatchBufferReset();        /* output can be very bogus due to escaping */
613
614         for (retry=0;retry<3;retry++) {
615                 ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
616                         "AT\r");
617                 if (wait_on(&err,10/*timeout*/)==GE_NONE)
618                         return(err);    /* success */
619                 }
620         /* Hmm, we've probably stucked the phone :-(
621          */
622         return(err);    /* failure */
623 }
624
625 static void ATHW_PhoneSetup_CMGF1_Detect(void)
626 {
627         ATHW_PhoneSetup_CMGF1_Detect_core();    /* errors of the detection are ignored */
628         ATHW_PhoneSetup_CMGS_Reset();
629         ATHW_SMS_CMGF01_post();         /* turn off echo */
630 #ifdef ATHW_DEBUG
631         printf("ATHW_PhoneSetup_CMGF1_Detect: ATHW_CMGS_CMGF1_8bit_HaveBinHex=%d\n",(int)ATHW_CMGS_CMGF1_8bit_HaveBinHex);
632 #endif
633 }
634
635 static GSM_Error ATHW_PhoneSetup_CMGFSet(int CMGF)
636 {
637         ATHW_CurrentCMGF=-1;
638         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
639                 "AT+CMGF=%d\r",CMGF);
640         ATHW_CurrentCMGF=CMGF;
641
642         if (CMGF==1)
643                 ATHW_PhoneSetup_CMGF1_Detect();
644
645         return(GE_NONE);
646 }
647
648 static GSM_Error ATHW_PhoneSetup(void)
649 {
650 GSM_Error err;
651
652         ATHW_CurrentMemoryType=GMT_XX;          /* invalidate */
653
654         ATHW_PhoneSetup_CMGS_Reset();
655
656         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,
657                 "AT&F\r");
658
659         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
660                 "ATE0\r");
661         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
662                 "AT+CSDH=1\r");
663         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
664                 "AT+CMEE=1\r");
665         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
666                 "AT+CRC=1\r");          /* request "+CRING: VOICE" reporting */
667
668         /* Try to detect Siemens M20 with M1 backward compatibility mode */
669         ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
670                 "AT+CSMS=128\r");
671         if (GE_NONE==wait_on(&err,10/*timeout*/)) {
672                 ATHW_CurrentSMSCPrefix=true;            /* it will be true ASAP +CMGS=0 is done */
673                 ATHW_CurrentSMSCPrefix_force=true;
674                 }
675         ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
676                 "AT+CSMS=0\r");                         /* turn on ATHW_CurrentSMSCPrefix, if possible */
677
678 #ifndef DISABLE_CMGF0
679         if (GE_NONE!=ATHW_PhoneSetup_CMGFSet(0))
680 #endif
681                 ATHW_PhoneSetup_CMGFSet(1);
682
683         ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
684                 "AT^SMGR=?\r");         /* it should just return "\nOK\n" */
685         ATHW_HaveSiemensMGR=(GE_NONE==wait_on(&err,10/*timeout*/));
686         ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
687                 "AT^SMGL=?\r");         /* it should just return "\nOK\n" */
688         ATHW_HaveSiemensMGL=(GE_NONE==wait_on(&err,10/*timeout*/));
689
690         ATHW_HaveCNMI=true;
691         ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
692                 "AT+CNMI=2,1\r");               /* <mode>=2 (buffer when data active), <mt>=1 (+CMTI codes) */
693         if (GE_NONE!=wait_on(&err,10/*timeout*/)) {
694                 ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
695                         "AT+CNMI=1,1\r");               /* <mode>=1 (discard when data active), <mt>=1 (+CMTI codes) */
696                 if (GE_NONE!=wait_on(&err,10/*timeout*/))
697                         ATHW_HaveCNMI=false;
698                 }
699         ATHW_CNMI_count=-1;     /* unknown yet */
700
701         ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
702                 "AT+COPS=3,2\r");               /* <mode>=3 (just set <format>), <format>=2 (numeric) */
703         wait_on(&err,10/*timeout*/);    /* error ignored, GetNetworkInfo will find the failure itself */
704
705         /* Enable LAC+CellID returns, it will also disable unsolicited changes reporting
706          * but we ignore it successfuly.
707          */
708         ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
709                 "AT+CREG=2\r");
710         wait_on(&err,10/*timeout*/);    /* error ignored, GetNetworkInfo will find the failure itself */
711
712         ATHW_GetPhoneInfo();
713
714         return(GE_NONE);
715 }
716
717
718 static void ATHW_RX_Char(char rx_byte);
719 static void ATHW_SigHandler(int status);
720 static bool ATHW_OpenSerial(void);
721
722 GSM_Phone phone_at_hw;  /* forward declaration */
723
724 /* Initialise variables and state machine. */
725
726 static GSM_Error ATHW_Init(GSM_Data *data, GSM_Statemachine *state)
727 {
728         RequestTerminate = false;
729         CallPassup = NULL;
730
731         /* Create and start main thread. */
732
733 #ifdef WIN32
734 {
735 int rtn;
736         rtn = ! OpenConnection(State->Link.PortDevice,ATHW_RX_Char,ATHW_KeepAliveProc);
737         if (rtn != 0) {
738                 return(GE_INTERNALERROR);
739 }
740 #else
741         SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
742         if (!ATHW_OpenSerial())
743                 return(GE_INTERNALERROR);
744 #endif
745
746         ATHW_ERR_WRAPPER(ATHW_PhoneSetup());
747
748         return (GE_NONE);
749 }
750
751 #if __unices__
752 /* thread for handling incoming data */
753 void ATHW_SelectLoop()
754 {
755         int err;
756         fd_set readfds;
757         struct timeval timeout;
758
759         FD_ZERO(&readfds);
760         FD_SET(device_portfd, &readfds);
761         /* set timeout to 15 seconds */
762         timeout.tv_sec=15;
763         timeout.tv_usec=0;
764         while (!RequestTerminate) {
765                 err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
766                 /* call singal handler to process incoming data */
767                 if ( err > 0 ) ATHW_SigHandler(0);
768                 else if (err == -1) perror("Error in SelectLoop");
769         }
770 }
771 #endif
772
773 static GSM_Error ATHW_AnswerCall(GSM_Data *data, GSM_Statemachine *state)
774 {
775         /* data->CallNo not present up to Nokia 9110i */
776         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"ATA\r");
777         return(GE_NONE);
778 }
779
780 /* Applications should call ATHW_Terminate to shut down the ATHW thread and
781    close the serial port. */
782
783 static GSM_Error ATHW_Terminate(GSM_Data *data, GSM_Statemachine *state)
784 {
785         /* Request termination of thread */
786         RequestTerminate = true;
787
788 #ifndef WIN32
789         /* Now wait for thread to terminate. */
790         pthread_join(Thread, NULL);
791
792         /* Close serial port. */
793         device_close();
794
795 #else
796         CloseConnection();
797 #endif
798
799         return(GE_NONE);
800 }
801
802 /* +CSQ: Signal Quality
803  *      CurrentRFLevel = 1st arg <rssi>
804  */
805 static char *ATHW_RX_Patrol_CSQ(char *after)
806 {
807 long l;
808
809         l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
810         if (l>=0 && l<=31)
811                 *CurrentRFLevel=l;
812
813         return(after);  /* eat line */
814 }
815
816 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSQ_struct=
817         { "\n+CSQ:", ATHW_RX_Patrol_CSQ };
818
819 static GSM_Error ATHW_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
820 {
821         CurrentRFLevel=data->RFLevel;
822
823         ATHW_TX_SENDCOMMAND_WAIT_ON(&GetRFLevelError/*errorcodep*/,30/*timeout*/,&ATHW_RX_Patrol_CSQ_struct,"AT+CSQ\r");
824
825         if (*data->RFUnits!=GRF_CSQ) {
826                 *data->RFUnits = GRF_Percentage;                /* required by xgnokii_lowlevel.c */
827                 *data->RFLevel = (*data->RFLevel)*100/31;       /* +CSQ */
828                 }
829
830         return (GE_NONE);
831 }
832
833 /* +CBC: Battery Charge
834  *      CurrentPowersource  = 1st arg <bcs>
835  *      CurrentBatteryLevel = 2nd arg <bcl>
836  */
837 static char *ATHW_RX_Patrol_CBC(char *after)
838 {
839 long l;
840
841         if (CurrentPowersource) {
842                 l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
843                 if (l==0 || l==1)
844                         *CurrentPowersource=(l==1 ? GPS_ACDC : GPS_BATTERY);
845                 }
846
847         if (CurrentBatteryLevel) {
848                 l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
849                 if (l>=0 && l<=100)
850                         *CurrentBatteryLevel=l;
851                 }
852
853         return(after);  /* eat line */
854 }
855
856 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CBC_struct=
857         { "\n+CBC:", ATHW_RX_Patrol_CBC };
858
859 static GSM_Error ATHW_GetBatteryLevel(GSM_Data *data, GSM_Statemachine *state)
860 {
861         CurrentBatteryLevel=data->BatteryLevel;
862         *data->BatteryUnits = GBU_Percentage;
863
864         ATHW_TX_SendCommand(&GetBatteryLevelError/*errorcodep*/,&ATHW_RX_Patrol_CBC_struct,"AT+CBC\r");
865         if (GE_NONE!=wait_on(&GetBatteryLevelError,30/*timeout*/)) {
866                 /* We are probably on AC powered device such as Nokia CardPhone
867                  */
868                 *CurrentBatteryLevel=100;
869                 }
870
871
872         return (GE_NONE);
873 }
874
875 static GSM_Error ATHW_GetPowersource(GSM_Data *data, GSM_Statemachine *state)
876 {
877         CurrentPowersource=data->PowerSource;
878
879         ATHW_TX_SendCommand(&GetPowersourceError/*errorcodep*/,&ATHW_RX_Patrol_CBC_struct,"AT+CBC\r");
880         if (GE_NONE!=wait_on(&GetPowersourceError,30/*timeout*/)) {
881                 /* We are probably on AC powered device such as Nokia CardPhone
882                  */
883                 *CurrentPowersource=GPS_ACDC;
884                 }
885
886         return (GE_NONE);
887 }
888
889 static GSM_Error ATHW_DialVoice(GSM_Data *data, GSM_Statemachine *state)
890 {
891         /* "ATDnumber;" is notation supported at least on Nokia up to 9110i
892          * We need to wait for establishing a connection as other commands would otherwise break it!
893          */
894         ATHW_TX_SENDCOMMAND_WAIT_ON(&DialVoiceError/*errorcodep*/,600/*timeout*/,NULL/*patrol*/,"ATD%s;\r",data->VoiceNumber);
895
896         return(GE_NONE);
897 }
898
899 /* Dial a data call - type specifies request to use: 
900      type 0 should normally be used
901      type 1 should be used when calling a digital line - corresponds to ats35=0
902      Maybe one day we'll know what they mean!
903          FIXME: ATHW currently ignores it - what value should be used for S35?
904 */
905
906 static GSM_Error ATHW_DialData(GSM_Data *data, GSM_Statemachine *state)
907 {
908         CallPassup=data->DataCallPassUp;
909
910         switch (*data->DataType) {
911         case 0: /* FALLTHRU */
912         case 1: /* FALLTHRU */
913         default:
914                 break;
915                 break;
916         case -1:   /* Just used to set the call passup */
917                 return GE_NONE;
918                 break;
919         }
920
921         ATHW_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,"ATD%s\r",data->DataNumber);
922         return(GE_NONE);
923 }
924
925 static GSM_Error ATHW_GetIncomingCallNr(GSM_Data *data, GSM_Statemachine *state)
926 {
927         if (*CurrentIncomingCall != ' ') {
928                 strcpy(data->IncomingCallNr, CurrentIncomingCall);
929                 return GE_NONE;
930         } else return GE_BUSY;
931 }
932
933 static GSM_Error ATHW_CancelCall(GSM_Data *data, GSM_Statemachine *state)
934 {
935         ATHW_TX_SENDCOMMAND_WAIT_ON(&CancelCallError/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,"ATH\r");
936
937         return(GE_NONE);
938 }
939
940 /* messagecenter->No" is NOT set as it is input argument */
941 static void ATHW_MessageCenterClear(GSM_MessageCenter *messagecenter)
942 {
943         messagecenter->Name[0]='\0';            /* not present up to Nokia 9110i */
944         messagecenter->Recipient[0]='\0';       /* not present up to Nokia 9110i */
945         messagecenter->Number[0]='\0';          /* default */
946         messagecenter->Format=GSMF_Text;        /* default */
947         messagecenter->Validity=GSMV_72_Hours;  /* default */
948 }
949
950 /* +CSCA:
951  *      MessageCenter->Number = 1st arg <sca>
952  */
953 static char *ATHW_RX_Patrol_CSCA(char *after)
954 {
955         ATHW_EXTRACTSTRING(CurrentMessageCenter->Number,after/*src*/,0/*element_no*/);
956
957         return(after);  /* eat line */
958 }
959
960 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSCA_struct=
961         { "\n+CSCA:", ATHW_RX_Patrol_CSCA };
962
963 /* +CSMP:
964  *      CurrentMessageCenter->Format   = 3rd arg <pid>
965  *      CurrentMessageCenter->Validity = 2nd arg <vp>
966  */
967 static char *ATHW_RX_Patrol_CSMP(char *after)
968 {
969 long l;
970
971         l=ATHW_ExtractNumber(after/*src*/,2/*element_no*/);
972         if (l>=0 && l<0x100)
973                 CurrentMessageCenter->Format=(GSM_SMSMessageFormat)l;
974
975         l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
976         if (l>=0 && l<0x100)
977                 CurrentMessageCenter->Validity=(GSM_SMSMessageValidity)l;
978
979         return(after);  /* eat line */
980 }
981
982 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSMP_struct=
983         { "\n+CSMP:", ATHW_RX_Patrol_CSMP };
984   
985 /* This function sends to the mobile phone a request for the SMS Center */
986
987 static GSM_Error ATHW_GetSMSCenter(GSM_Data *data, GSM_Statemachine *state)
988 {
989         CurrentMessageCenter = data->MessageCenter;
990
991         if (CurrentMessageCenter->No!=1)        /* "CurrentMessageCenter->No" not present up to Nokia 9110i */
992                 return(GE_INTERNALERROR);       /* FIXME: some better code? */
993         ATHW_MessageCenterClear(CurrentMessageCenter);
994
995         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMessageCenterError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CSCA_struct,"AT+CSCA?\r");
996         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMessageCenterError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CSMP_struct,"AT+CSMP?\r");
997
998         printf("message center OK: %s\n",CurrentMessageCenter->Number);
999         return(GE_NONE);
1000 }
1001
1002 /* This function set the SMS Center profile on the phone. */
1003
1004 static GSM_Error ATHW_SetSMSCenter(GSM_Data *data, GSM_Statemachine *state)
1005 {
1006         CurrentMessageCenter = data->MessageCenter;
1007         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMessageCenterError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
1008                 "AT+CSCA=%s\r",ATHW_Enquote(CurrentMessageCenter->Number));
1009         return(GE_NONE);
1010 }
1011
1012 static int ATHW_RX_Patrol_SiemensMGL_count;
1013
1014 static char *ATHW_RX_Patrol_SiemensMGL(char *after)
1015 {
1016 char *s;
1017
1018         /* There may be left string from AT+CMGL=? which would confuse us!
1019          */
1020         for (s=after;isspace(*s);s++);
1021         if (*s=='(')
1022                 return(after);  /* eat line */
1023                 
1024         /* Lines with our wanted "REC UNREAD" should be already filtered by the request
1025          */
1026         ATHW_RX_Patrol_SiemensMGL_count++;
1027
1028         return(after);  /* eat line */
1029 }
1030
1031 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_SiemensMGL_struct=
1032         { "\n^SMGL:", ATHW_RX_Patrol_SiemensMGL };
1033
1034 static void ATHW_Update_CNMI_count(void)
1035 {
1036 const char *state;
1037
1038         if (!ATHW_HaveSiemensMGL)
1039                 return;
1040
1041         switch (ATHW_CurrentCMGF) {
1042         case 0: state="0"; break;
1043         case 1: state=ATHW_Enquote("REC UNREAD"); break;
1044         default:
1045                 return; /* not supported */
1046         }
1047
1048         ATHW_RX_Patrol_SiemensMGL_count=0;
1049         ATHW_TX_SendCommand(&CurrentSMSStatusError/*errorcodep*/,&ATHW_RX_Patrol_SiemensMGL_struct/*patrol*/,
1050                 "AT^SMGL=%s\r",state);
1051         if (GE_NONE==wait_on(&CurrentSMSStatusError,600/*timeout*/))
1052                 ATHW_CNMI_count=ATHW_RX_Patrol_SiemensMGL_count;        /* otherwise not updated */
1053 }
1054
1055 /* +CPMS:
1056  *      CurrentSMSStatus->Used   = 2nd arg <used1>
1057  *      CurrentSMSStatus->Slots  = 3nd arg <total1>
1058  */
1059 static char *ATHW_RX_Patrol_CPMS(char *after)
1060 {
1061 long l;
1062
1063         l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
1064         if (l>=0 && l<INT_MAX)
1065                 CurrentSMSStatus->Used=l;
1066
1067         l=ATHW_ExtractNumber(after/*src*/,2/*element_no*/);
1068         if (l>=0 && l<INT_MAX)
1069                 CurrentSMSStatus->Slots=l;
1070
1071         return(after);  /* eat line */
1072 }
1073
1074 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPMS_struct=
1075         { "\n+CPMS:", ATHW_RX_Patrol_CPMS };
1076
1077 /* Note: "Status->UnRead" cannot be detected at all up to Nokia 9110i
1078  * as its GEOS always reads the incoming message immediately.
1079  * On some other devices it could be probably solved by: AT+CMGL="REC UNREAD"
1080  */
1081 static GSM_Error ATHW_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
1082 {
1083         CurrentSMSStatus = data->SMSStatus;
1084         if (ATHW_CNMI_count<0)
1085                 ATHW_Update_CNMI_count();
1086         CurrentSMSStatus->UnRead=(ATHW_CNMI_count<0 ? 0 : ATHW_CNMI_count);     /* not present up to Nokia 9110i */
1087         CurrentSMSStatus->Used =10;             /* default */
1088         CurrentSMSStatus->Slots=10;             /* default */
1089         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CPMS_struct,
1090                 "AT+CPMS?\r");
1091         return(GE_NONE);
1092 }
1093
1094 static GSM_Error ATHW_GetImei(GSM_Data *data, GSM_Statemachine *state)
1095 {
1096         if (*IMEI) {
1097                 strcpy(data->Imei,IMEI);
1098                 return (GE_NONE);
1099         } else return (GE_TRYAGAIN);
1100 }
1101
1102 static GSM_Error ATHW_GetRevision(GSM_Data *data, GSM_Statemachine *state)
1103 {
1104         if (*Revision) {
1105                 strcpy(data->Revision,Revision);
1106                 return (GE_NONE);
1107         } else return (GE_TRYAGAIN);
1108 }
1109
1110 static GSM_Error ATHW_GetModel(GSM_Data *data, GSM_Statemachine *state)
1111 {
1112         if (*Model) {
1113                 strcpy(data->Model,Model);
1114                 return (GE_NONE);
1115         } else return (GE_TRYAGAIN);
1116 }
1117
1118 static GSM_Error ATHW_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
1119 {
1120         if (*Model) {
1121                 strcpy(data->Manufacturer,Manufacturer);
1122                 return (GE_NONE);
1123         } else return (GE_TRYAGAIN);
1124 }
1125
1126 /* This function translates GMT_MemoryType to the string for AT+CPBS
1127  * Nokia 9000i: ("SM")
1128  * Siemens M20: ("SM","FD","LD","RC","ON","ME","MC","MT")
1129  */
1130
1131 #define GETMEMORYTYPE_ENTRY(code) \
1132                 case GMT_##code: return( #code );
1133
1134 static const char *ATHW_GetMemoryType(GSM_MemoryType memory_type)
1135 {
1136         switch (memory_type) {
1137         GETMEMORYTYPE_ENTRY(ME)
1138         GETMEMORYTYPE_ENTRY(SM)
1139         GETMEMORYTYPE_ENTRY(FD)
1140         GETMEMORYTYPE_ENTRY(ON)
1141         GETMEMORYTYPE_ENTRY(EN)
1142         GETMEMORYTYPE_ENTRY(DC)
1143         GETMEMORYTYPE_ENTRY(RC)
1144         GETMEMORYTYPE_ENTRY(MC)
1145         GETMEMORYTYPE_ENTRY(LD)
1146         GETMEMORYTYPE_ENTRY(MT)
1147         GETMEMORYTYPE_ENTRY(TA)
1148         GETMEMORYTYPE_ENTRY(CB)
1149                 default:     return(NULL);
1150         }
1151 }
1152
1153 #define ATHW_GETMEMORYTYPE(dst,memory_type) \
1154         do { \
1155                 (dst)=ATHW_GetMemoryType((memory_type)); \
1156                 if (!(dst)) \
1157                         return(GE_INVALIDMEMORYTYPE); \
1158         } while (0)
1159
1160 static GSM_Error ATHW_SelectPhonebookMemory(GSM_MemoryType memory_type)
1161 {
1162 const char *atmemtype;
1163
1164         if (memory_type==ATHW_CurrentMemoryType)
1165                 return(GE_NONE);
1166         ATHW_CurrentMemoryType=GMT_XX;
1167
1168         ATHW_GETMEMORYTYPE(atmemtype,memory_type);
1169         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhonebookError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
1170                 "AT+CPBS=%s\r",ATHW_Enquote(atmemtype));
1171
1172         ATHW_CurrentMemoryType=memory_type;
1173
1174         return(GE_NONE);
1175 }
1176
1177 /* +CPBR: (GetMemoryStatus)
1178  *      CurrentMemoryStatus->Free=1st arg (1-<index>)
1179  */
1180 static char *ATHW_RX_Patrol_CPBR_GetMemoryStatus(char *after)
1181 {
1182 char buf[32];
1183 int num_1,num_2;
1184
1185         ATHW_EXTRACTSTRING(buf,after/*src*/,0/*element_no*/);
1186         if (2==sscanf(buf,"(%d-%d)",&num_1,&num_2)) {
1187                 CurrentMemoryStatus->Used=0;            /* not present up to Nokia 9110i, we ignore it now */
1188                 CurrentMemoryStatus->Free=num_2;
1189                 }
1190
1191         return(after);  /* eat line */
1192 }
1193
1194 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPBR_GetMemoryStatus_struct=
1195         { "\n+CPBR:", ATHW_RX_Patrol_CPBR_GetMemoryStatus };
1196
1197 static GSM_Error ATHW_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
1198 {
1199         CurrentMemoryStatus=data->MemoryStatus;
1200
1201         ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentMemoryStatus->MemoryType));
1202
1203         CurrentMemoryStatus->Used=0;            /* default */
1204         CurrentMemoryStatus->Free=0;            /* default */
1205
1206         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMemoryStatusError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CPBR_GetMemoryStatus_struct,
1207                 "AT+CPBR=?\r");
1208
1209         return(GE_NONE);
1210 }
1211
1212 static void ATHW_DateTimeSetCurrent(GSM_DateTime *datetime)
1213 {
1214 time_t current=time(NULL);
1215 struct tm *tm=localtime(&current);
1216
1217         datetime->AlarmEnabled=false;
1218
1219         datetime->Year  =tm->tm_year+1900;
1220         datetime->Month =tm->tm_mon+1;
1221         datetime->Day   =tm->tm_mday;
1222         datetime->Hour  =tm->tm_hour;
1223         datetime->Minute=tm->tm_min;
1224         datetime->Second=tm->tm_sec;
1225
1226         datetime->Timezone=timezone;
1227 }
1228
1229 /* +CPBR: (ReadPhonebook)
1230  *      CurrentPhonebookEntry->Name  =4th arg <text>
1231  *      CurrentPhonebookEntry->Number=2nd arg <number>
1232  */
1233 static char *ATHW_RX_Patrol_CPBR_ReadPhonebook(char *after)
1234 {
1235         CurrentPhonebookEntry->Empty=false;
1236         ATHW_EXTRACTSTRING(CurrentPhonebookEntry->Name  ,after/*src*/,3/*element_no*/);
1237         ATHW_EXTRACTSTRING(CurrentPhonebookEntry->Number,after/*src*/,1/*element_no*/);
1238
1239         return(after);  /* eat line */
1240 }
1241
1242 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPBR_ReadPhonebook_struct=
1243         { "\n+CPBR:", ATHW_RX_Patrol_CPBR_ReadPhonebook };
1244
1245 /* Routine to get specifed phone book location.  Designed to be called by
1246  * application.  Will block until location is retrieved or a timeout/error
1247  * occurs.
1248  */
1249 static GSM_Error ATHW_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
1250 {
1251         CurrentPhonebookEntry=data->PhonebookEntry;
1252
1253         ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentPhonebookEntry->MemoryType));
1254
1255         /* We may get just OK response (Siemens M20) which means empty location
1256          */
1257         CurrentPhonebookEntry->Empty=true;
1258         CurrentPhonebookEntry->Group=0;                         /* not present up to Nokia 9110i */
1259         ATHW_DateTimeSetCurrent(&CurrentPhonebookEntry->Date);  /* not present up to Nokia 9110i */
1260         CurrentPhonebookEntry->SubEntriesCount=0;               /* not present up to Nokia 9110i */
1261         CurrentPhonebookEntry->Name[0]='\0';                    /* default */
1262         CurrentPhonebookEntry->Number[0]='\0';                  /* default */
1263
1264         ATHW_TX_SendCommand(&CurrentPhonebookError/*errorcodep*/,&ATHW_RX_Patrol_CPBR_ReadPhonebook_struct,
1265                 "AT+CPBR=%d\r",CurrentPhonebookEntry->Location);
1266         if (GE_NONE!=wait_on(&CurrentPhonebookError,10/*timeout*/)) {
1267                 /* Nokia 9000i returns ATHW_CME_NOT_FOUND error code for empty locations
1268                  */
1269                 if (ATHW_CME_NOT_FOUND!=ATHW_RX_Patrol_CME_ERROR_code)
1270                         return(CurrentPhonebookError);
1271                 return(GE_NONE);
1272                 }
1273
1274         return(GE_NONE);
1275 }
1276
1277
1278 /* Routine to write phonebook location in phone. Designed to be called by
1279    application code. Will block until location is written or timeout
1280    occurs. */
1281
1282 static GSM_Error ATHW_WritePhonebook(GSM_Data *data, GSM_Statemachine *state)
1283 {
1284         CurrentPhonebookEntry=data->PhonebookEntry;
1285
1286         ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentPhonebookEntry->MemoryType));
1287
1288         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhonebookError/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,
1289                 (CurrentPhonebookEntry->Empty ? "AT+CPBW=%d\r" : "AT+CPBW=%d,%s,,%s\r"),
1290                 CurrentPhonebookEntry->Location,
1291                 ATHW_Enquote(CurrentPhonebookEntry->Number),
1292                 ATHW_Enquote(CurrentPhonebookEntry->Name)
1293                 );
1294
1295         return(GE_NONE);
1296 }
1297
1298 static const char *ATHW_SMStoStat(GSM_SMSMessage *SMS)
1299 {
1300         switch (CurrentSMSMessage->Type) {
1301         case GST_MT:
1302                 switch (CurrentSMSMessage->Status) {
1303                 case GSS_NOTSENTREAD:
1304                         return("REC UNREAD");
1305                 case GSS_SENTREAD:
1306                         return("REC READ");
1307                 }
1308                 return(NULL);
1309         case GST_MO:
1310                 switch (CurrentSMSMessage->Status) {
1311                 case GSS_NOTSENTREAD:
1312                         return("STO UNSENT");
1313                 case GSS_SENTREAD:
1314                         return("STO SENT");
1315                 }
1316                 return(NULL);
1317         default:
1318                 return(NULL);
1319         }
1320         /* NOTREACHED */
1321 }
1322
1323 /* RETURN: Success of recognizing "SMSstat"
1324  */
1325 static bool ATHW_StattoSMS(GSM_SMSMessage *SMS,const char *SMSstat)
1326 {
1327         /**/ if (!strcmp(SMSstat,"0") || !strcmp(SMSstat,"REC UNREAD")) {
1328                 SMS->Type=GST_MT;
1329                 SMS->Status=GSS_NOTSENTREAD;
1330                 }
1331         else if (!strcmp(SMSstat,"1") || !strcmp(SMSstat,"REC READ")) {
1332                 SMS->Type=GST_MT;
1333                 SMS->Status=GSS_SENTREAD;
1334                 }
1335         else if (!strcmp(SMSstat,"2") || !strcmp(SMSstat,"STO UNSENT")) {
1336                 SMS->Type=GST_MO;
1337                 SMS->Status=GSS_NOTSENTREAD;
1338                 }
1339         else if (!strcmp(SMSstat,"3") || !strcmp(SMSstat,"STO SENT")) {
1340                 SMS->Type=GST_MO;
1341                 SMS->Status=GSS_SENTREAD;
1342                 }
1343         else return(false);     /* not recognized! */
1344         return(true);
1345 }
1346
1347 /* scts=="01/10/25,02:28:18+08", scts WILL BE DESTROYED!
1348  */
1349 static bool ATHW_SCTStoSMS_CMGF1(GSM_SMSMessage *SMS,char *scts)
1350 {
1351 GSM_DateTime *DateTime=&SMS->Time;
1352 bool timezone_minus;
1353 const char *fmt="%d/%d/%d,%d:%d:%d+%d"; /* trailing '\0' IS used! */
1354 int i;
1355
1356         if (strlen(scts)!=20)
1357                 return(false);
1358         if ((timezone_minus=(scts[17]=='-')))
1359                 scts[17]='+';
1360         i=0;
1361         do {
1362                 if (!isdigit(scts[i++]))
1363                         return(false);
1364                 if (!isdigit(scts[i++]))
1365                         return(false);
1366                 if (scts[i]!=fmt[i])
1367                         return(false);
1368                 } while (fmt[i++]);
1369         /* string is completely valid now */
1370
1371         sscanf(scts,fmt,
1372                         &DateTime->Year,
1373                         &DateTime->Month,
1374                         &DateTime->Day,
1375                         &DateTime->Hour,
1376                         &DateTime->Minute,
1377                         &DateTime->Second,
1378                         &DateTime->Timezone);
1379         if (DateTime->Year>=85)
1380                 DateTime->Year-=100;
1381         DateTime->Year+=2000;
1382         if (timezone_minus)
1383                 DateTime->Timezone=-DateTime->Timezone;
1384         return(true);
1385 }
1386
1387 static void ATHW_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
1388 {
1389         switch ((dcs&0xF0)>>4) {
1390                 case 0x0:
1391                         switch (dcs&0x0F) {
1392                                 case 0x0:
1393                                         CurrentSMSMessage->EightBit=false;
1394                                         break;
1395                                 }
1396                         break;
1397                 case 0xF:
1398                         CurrentSMSMessage->EightBit=!!(dcs&0x04);       /* bit 2 */
1399                         CurrentSMSMessage->Class=(dcs&0x03);            /* bits 0 & 1 */
1400                         break;
1401                 }
1402 }
1403
1404 static bool ATHW_SCTStoSMS_CMGF0(GSM_SMSMessage *SMS,unsigned char *scts)
1405 {
1406 GSM_DateTime *DateTime=&SMS->Time;
1407 int *fields[]={         /* FIXME: offsetof() would be nice here but currently not supported by Gnokii */
1408                 &DateTime->Year,
1409                 &DateTime->Month,
1410                 &DateTime->Day,
1411                 &DateTime->Hour,
1412                 &DateTime->Minute,
1413                 &DateTime->Second,
1414                 &DateTime->Timezone,
1415                 };
1416 int i;
1417 unsigned char digit0,digit1;
1418
1419         for (i=0;i<ARRAY_LEN(fields);i++,scts++) {
1420                 if (0
1421                         || (digit0= (*scts)    &0x0F) > 9
1422                         || (digit1=((*scts)>>4)&0x0F) > 9
1423                     )
1424                         return(false);
1425                 (*fields[i])=10*digit0 + digit1;
1426                 }
1427         /* scts is completely valid now */
1428
1429         if (DateTime->Year>=85)
1430                 DateTime->Year-=100;
1431         DateTime->Year+=2000;
1432         return(true);
1433 }
1434
1435 /* Value MUST match ((fo>>3)&0x03) !
1436  */
1437 enum ATHW_FO_Validity {
1438         ATHW_FOV_None    =0,
1439         ATHW_FOV_Reserved=1,
1440         ATHW_FOV_Relative=2,
1441         ATHW_FOV_Absolute=3,
1442         };
1443 static const size_t ATHW_FO_Validity_sizes[]={
1444         0,      /* ATHW_FOV_None */
1445         0,      /* ATHW_FOV_Reserved - not valid */
1446         1,      /* ATHW_FOV_Relative */
1447         7,      /* ATHW_FOV_Absolute */
1448         };
1449
1450 /* RETURN: Success of recognizing "SMSstat"
1451  */
1452 static void ATHW_FOtoSMS(GSM_SMSMessage *SMS,enum ATHW_FO_Validity *validityp,unsigned char fo)
1453 {
1454         switch (fo&0x03) {
1455                 case 0: SMS->Type=GST_MT; break;
1456                 case 1: SMS->Type=GST_MO; break;
1457                 default: /* FIXME: value 2 and 3? */
1458                 }
1459
1460         /* bit 2 - reject duplices - not supported by Gnokii
1461          */
1462
1463         if (validityp)
1464                 *validityp=(enum ATHW_FO_Validity)((fo>>3)&0x03);
1465
1466         /* bit 5 - status report request - not supported by Gnokii
1467          */
1468
1469         SMS->UDHPresent=!!(fo&(1<<6));
1470
1471         SMS->ReplyViaSameSMSC=!!(fo&(1<<7));
1472 }
1473
1474 static GSM_Error ATHW_RX_Patrol_CMGR_CMGF0_core(char *after,char *messagetext,char *end)
1475 {
1476 unsigned char pdu[GNOKII_MAX_PDU_LENGTH],*pduend,*s;
1477 char *oada;
1478 size_t oada_size,udl;
1479 enum ATHW_FO_Validity FO_validity;
1480 unsigned udbits;        /* 7 or 8 bits in UserData */
1481
1482         if (end-messagetext>2*sizeof(pdu))
1483                 return(GE_INTERNALERROR);       /* input PDU hex too long! */
1484         if (!(pduend=SMS_BlockFromHex(pdu,messagetext,end-messagetext/*len*/)))
1485                 return(GE_INTERNALERROR);
1486         s=pdu;
1487         if (ATHW_CurrentSMSCPrefix) {
1488                 if (s >= pduend
1489                         || *s < 1/*type*/
1490                         || *s > 1/*type*/
1491                                 + ( sizeof(CurrentSMSMessage->MessageCenter.Number)
1492                                 -1/*'\0' termination*/
1493                                 +1 )/2/*bytes of rounded-up nibbles*/
1494                         || s +1/*type*/ +((*s)+1)/2/*bytes of rounded-up nibbles*/ > pduend     /* whole SMSC.Number must fit */
1495                         )
1496                         return(GE_INTERNALERROR);
1497                 if (!(s=SemiOctetUnpack(CurrentSMSMessage->MessageCenter.Number,sizeof(CurrentSMSMessage->MessageCenter.Number),
1498                                 s+1,2*((*s) -1/*type*/ ))))
1499                         return(GE_INTERNALERROR);               /* invalid SMSCenter */
1500                 }
1501         if (s +1/*<fo>*/ +1/*received number length*/ > pduend)
1502                 return(GE_INTERNALERROR);
1503         ATHW_FOtoSMS(CurrentSMSMessage,&FO_validity,*s++);
1504
1505         switch (CurrentSMSMessage->Type) {
1506
1507         case GST_MT:
1508                 oada     =CurrentSMSMessage->Sender;
1509                 oada_size=sizeof(CurrentSMSMessage->Sender);
1510                 break;
1511
1512         case GST_MO:
1513                 oada     =CurrentSMSMessage->Destination;
1514                 oada_size=sizeof(CurrentSMSMessage->Destination);
1515                 
1516                 /* message reference is really not interesting to be read */
1517                 if (s +1/*<mr>*/ > pduend)
1518                         return(GE_INTERNALERROR);
1519                 s++;
1520
1521                 break;
1522
1523         /* FIXME: GST_DR - Delivery Report - not supported!
1524          */
1525         default:
1526                 /* Unable to skip OA/DA, parsing would fail
1527                  */
1528                 return(GE_INTERNALERROR);
1529         }
1530
1531         if (oada) {
1532                 if (s + 1/*type*/ +((*s)+1)/2/*bytes of rounded-up nibbles*/ > pduend)
1533                         return(GE_INTERNALERROR);
1534                 if (!(s=SemiOctetUnpack(oada,oada_size,s+1,(*s)/*nibbles*/)))
1535                         return(GE_INTERNALERROR);               /* invalid OA/DA */
1536                 }
1537
1538         if (s +1/*<pid>*/ +1/*<dcs>*/ > pduend)
1539                 return(GE_INTERNALERROR);
1540
1541         /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
1542         CurrentSMSMessage->MessageCenter.Format=*s++;   /* <pid> */
1543
1544         ATHW_DCStoSMS(CurrentSMSMessage,*s++);          /* <dcs> */
1545
1546         switch (CurrentSMSMessage->Type) {
1547
1548         case GST_MT:            /* SCTS field */
1549                 if (s +7/*scts*/ > pduend)
1550                         return(GE_INTERNALERROR);
1551                 if (!ATHW_SCTStoSMS_CMGF0(CurrentSMSMessage,s))
1552                         return(GE_INTERNALERROR);
1553                 s+=7;
1554                 break;
1555
1556         case GST_MO:            /* VP field */
1557                 if (s +ATHW_FO_Validity_sizes[(unsigned)FO_validity] > pduend)
1558                         return(GE_INTERNALERROR);
1559                 switch (FO_validity) {
1560                 case ATHW_FOV_None:
1561                         break;
1562                 case ATHW_FOV_Reserved:
1563                         return(GE_INTERNALERROR);       /* not supported */
1564                 case ATHW_FOV_Relative:
1565                         CurrentSMSMessage->Validity=SMS_VP_to_Validity((GSM_SMSMessageValidity)*s);
1566                         break;
1567                 case ATHW_FOV_Absolute:
1568                         /* leave default, not supported by Gnokii */
1569                         break;
1570                 }
1571                 s+=ATHW_FO_Validity_sizes[(unsigned)FO_validity];
1572                 break;
1573
1574         default:
1575                 /* To be written when other types get supported in the above switch
1576                  */
1577         }
1578
1579         if (s +1/*udl*/ > pduend)
1580                 return(GE_INTERNALERROR);
1581
1582         /* We are pretty strict as UDL must exactly match the end of SMS
1583          * as the is the primary check for the good guess of SMSCenter presentness
1584          * during guessing by ATHW_RX_Patrol_CMGR_CMGF0_retrier().
1585          */
1586         udl=(*s++);
1587         udbits=(CurrentSMSMessage->EightBit ? 8 : 7);
1588         if (s +( udl*udbits +7/*round-up*/ )/8 != pduend)
1589                 return(GE_INTERNALERROR);
1590
1591         if (CurrentSMSMessage->UDHPresent) {
1592                 if (0
1593                                 || s +1/*sizeof(UDH[0])*/ > pduend
1594                                 || s +1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/ > pduend
1595                                 ||    1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/ > sizeof(CurrentSMSMessage->UDH)
1596                                 )
1597                         return(GE_INTERNALERROR);
1598                 memcpy(CurrentSMSMessage->UDH,s, 1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/ );
1599                 s+=1/*sizeof(UDH[0])*/ + *s/*UDH[0]*/;
1600                 udl-=(( 1/*sizeof(UDH[0])*/ +CurrentSMSMessage->UDH[0] )*8 +0/*round-down*/ )/udbits;
1601                 }
1602
1603         if (udl > (sizeof(CurrentSMSMessage->MessageText) -1/*terminating '\0'*/))
1604                 return(GE_INTERNALERROR);
1605         CurrentSMSMessage->MessageTextLength=udl;
1606         CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength]='\0';
1607
1608         if (udbits==8) {
1609                 memcpy(CurrentSMSMessage->MessageText,s,udl);
1610                 }
1611         else {  /* udhbits==7 */
1612                 UnpackEightBitsToSeven((7-(!CurrentSMSMessage->UDHPresent ? 0 : 1+CurrentSMSMessage->UDH[0]))%7,        /* offset */
1613                         pduend-s,       /*  in_length */
1614                         udl,            /* out_length */
1615                         s,              /* input */
1616                         CurrentSMSMessage->MessageText  /* output */
1617                         );
1618                 CurrentSMSMessage->MessageText[udl]='\0';
1619                 }
1620
1621         return(GE_NONE);
1622 }
1623
1624 /* This is just a variant of ATHW_SMS_CMGF0(), I was too lazy to generalize it
1625  */
1626 static GSM_Error ATHW_RX_Patrol_CMGR_CMGF0_retrier(char *after,char *messagetext,char *end)
1627 {
1628 GSM_Error err,err_first=GE_INTERNALERROR/*shut up GCC*/;
1629 int retry;
1630
1631         for (retry=0;retry<2;retry++) {
1632                 /* There is no need to give up trying decoding (=> retried>0 )
1633                  * by both ways as it does't cost us anything (not as in ATHW_SMS_CMGF0()).
1634                  */
1635                 if (GE_NONE==(err=ATHW_RX_Patrol_CMGR_CMGF0_core(after,messagetext,end),1/*retried*/)) {
1636                         ATHW_CurrentSMSCPrefix_force=true;
1637                         return(err);
1638                         }
1639                 if (ATHW_CurrentSMSCPrefix_force)
1640                         return(err);
1641                 if (!retry)
1642                         err_first=err;
1643                 ATHW_CurrentSMSCPrefix=!ATHW_CurrentSMSCPrefix;
1644                 }
1645         /* Return rather the first error code as it is more probable that
1646          * it was generated with valid ATHW_CurrentSMSCPrefix setting
1647          */
1648         return(err_first);
1649 }
1650
1651 /* +CMGR message reading will be finalized by "\nOK\n" which will terminate wait_on().
1652  * But invalid message data followed by "\nOK\n" are still invalid so we have to indicate it.
1653  */
1654 static void ATHW_RX_Patrol_CMGR_CMGF0(char *after,char *messagetext,char *end)
1655 {
1656 GSM_Error err;
1657
1658         err=ATHW_RX_Patrol_CMGR_CMGF0_retrier(after,messagetext,end);
1659         if (err!=GE_NONE)
1660                 CurrentSMSMessageError=err;
1661 }
1662
1663 /* +CMGR: (+CMGF==1)
1664  *      CurrentSMSMessage->Status      = 1st arg <stat>
1665  *      CurrentSMSMessage->Sender      = 2nd arg <oa>
1666  *      CurrentSMSMessage->Destination = 2nd arg <da>
1667  *      ...etc., see the code below
1668  *
1669  *      Type==GST_MT:
1670  *              +CMGR: <stat>,<oa>,<alpha>,<scts>,<tooa>,<fo>,<pid>,<dcs>     ,<sca>,<tosca>,<length>
1671  *                     0      1    2       3      4      5    6     7          8     9       10
1672  *      Type==GSM_MO:
1673  *              +CMGR: <stat>,<da>,<alpha>       ,<toda>,<fo>,<pid>,<dcs>,<vp>,<sca>,<tosca>,<length>
1674  *                     0      1    2              3      4    5     6     7    8     9       10
1675  */
1676 static void ATHW_RX_Patrol_CMGR_CMGF1(char *after,char *messagetext,char *end)
1677 {
1678 char buf[32];
1679 int fieldno_fo=-1,fieldno_dcs=-1;
1680 long l;
1681
1682         switch (CurrentSMSMessage->Type) {
1683
1684         case GST_MT:
1685                 ATHW_EXTRACTSTRING(CurrentSMSMessage->Sender,after/*src*/,1/*element_no*/);
1686
1687                 /* <scts> Service centre time stamp string format "01/10/25,02:28:18+08"
1688                  * Note: not present up to Nokia 9110i
1689                  */
1690                 ATHW_EXTRACTSTRING(buf                      ,after/*src*/,3/*element_no*/);
1691                 ATHW_SCTStoSMS_CMGF1(CurrentSMSMessage,buf/*scts*/);    /* error ignored, leave the default value */
1692
1693                 fieldno_fo=5;
1694                 fieldno_dcs=7;
1695                 break;
1696
1697         case GST_MO:
1698                 ATHW_EXTRACTSTRING(CurrentSMSMessage->Destination,after/*src*/,1/*element_no*/);
1699                 fieldno_fo=4;
1700                 fieldno_dcs=6;
1701
1702                 l=ATHW_ExtractNumber(after/*src*/,7/*element_no*/);
1703                 if (l>=0 && l<0x100)
1704                         CurrentSMSMessage->Validity=SMS_VP_to_Validity((GSM_SMSMessageValidity)l);
1705                 break;
1706
1707         default:
1708                 /* Not supported */
1709         }
1710
1711         if (fieldno_fo>=0) {
1712                 l=ATHW_ExtractNumber(after/*src*/,fieldno_fo/*element_no*/);
1713                 if (l>=0 && l<0x100)
1714                         ATHW_FOtoSMS(CurrentSMSMessage,
1715                                         NULL,   /* validityp - solved by GSM device in +CMGF==1 */
1716                                         l);
1717                 }
1718
1719         if (fieldno_dcs>=0) {
1720                 l=ATHW_ExtractNumber(after/*src*/,fieldno_dcs/*element_no*/);
1721                 if (l>=0 && l<0x100)
1722                         ATHW_DCStoSMS(CurrentSMSMessage,l);
1723                 }
1724
1725         ATHW_EXTRACTSTRING(CurrentSMSMessage->MessageCenter.Number,after/*src*/,8/*element_no*/);
1726
1727         CurrentSMSMessage->MessageTextLength=GNOKII_MIN(end-messagetext,sizeof(CurrentSMSMessage->MessageText)-1);
1728         memcpy(CurrentSMSMessage->MessageText,messagetext,CurrentSMSMessage->MessageTextLength);
1729         CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength]='\0';
1730 }
1731
1732 /* +CMGR:
1733  *      CurrentSMSMessage->Type        = 1st arg <stat>
1734  */
1735 static char *ATHW_RX_Patrol_CMGR(char *after)
1736 {
1737 char buf[32],*end,*messagetext;
1738
1739         end=strchr(after,'\n');
1740         if (!end++)
1741                 return(after);          /* assert, INTERNAL! */
1742         if (!*end)
1743                 return(NULL);           /* need data */
1744         if (*end=='\n')                 /* 2nd optional '\n' */
1745                 end++;                  /* '\n' should be really double as it was "\r\n" from the device */
1746         messagetext=end;
1747         if (!(end=strchr(end,'\n')))
1748                 return(NULL);           /* need data */
1749         /* Now we have read "+CMGR:*\n[\n]*\n" */
1750
1751         /* 1st arg <stat> is numeric(+CMGF==0)/alpha(+CMGF==1), ATHW_StattoSMS parses it all
1752          */
1753         ATHW_EXTRACTSTRING(buf,after/*src*/,0/*element_no*/);
1754         ATHW_StattoSMS(CurrentSMSMessage,buf);                  /* error ignored: what to do if unknown? */;
1755
1756         switch (ATHW_CurrentCMGF) {
1757                 case 0: ATHW_RX_Patrol_CMGR_CMGF0(after,messagetext,end); break;
1758                 case 1: ATHW_RX_Patrol_CMGR_CMGF1(after,messagetext,end); break;
1759                 }
1760
1761         return(++end);  /* eat the whole SMS block, INCLUDING the trailing '\n' */
1762 }
1763
1764 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGR_struct=
1765         { "\n+CMGR:", ATHW_RX_Patrol_CMGR };
1766
1767 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_SiemensMGR_struct=
1768         { "\n^SMGR:", ATHW_RX_Patrol_CMGR };
1769
1770 /* Currently we always set the first two memory types as the syntax with empty
1771  * preceding elements is not supported at least by Nokia 9110i & Siemens M20.
1772  * Ideal case would be to first query the state by AT+CPMS? and then the settings
1773  * would be written back.
1774  * We never need to set the third argument, for example on Nokia 9110i it even isn't
1775  * supported for "SM" memory type!
1776  */
1777 static GSM_Error ATHW_SMS_SelectMemoryType(GSM_SMSMessage *SMS)
1778 {
1779 const char *atmemtype;
1780 const char *atmemtype_quoted;
1781
1782         ATHW_GETMEMORYTYPE(atmemtype,SMS->MemoryType);
1783         atmemtype_quoted=ATHW_Enquote(atmemtype);
1784
1785         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
1786                 "AT+CPMS=%s,%s\r",atmemtype_quoted,atmemtype_quoted);
1787
1788         return(GE_NONE);
1789 }
1790
1791 static GSM_Error ATHW_GetSMS(GSM_Data *data, GSM_Statemachine *state)
1792 {
1793         CurrentSMSMessage = data->SMSMessage;
1794
1795         ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage)); /* select <mem1> - reading/deleting */
1796
1797         ATHW_DateTimeSetCurrent(&CurrentSMSMessage->Time);              /* not present up to Nokia 9110i */
1798         ATHW_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime);          /* not present up to Nokia 9110i */
1799         CurrentSMSMessage->MessageTextLength=0;                         /* default */
1800         CurrentSMSMessage->Validity=72/*hours*/*60;                     /* default */
1801         CurrentSMSMessage->UDHPresent=false;                            /* default */
1802         CurrentSMSMessage->MessageText[0]='\0';                         /* default */
1803
1804         ATHW_MessageCenterClear(&CurrentSMSMessage->MessageCenter);     /* default */
1805         CurrentSMSMessage->MessageCenter.No=0;                          /* default - input for GetSMSCenter */
1806
1807         CurrentSMSMessage->Sender[0]='\0';                              /* default */
1808         CurrentSMSMessage->Destination[0]='\0';                         /* default */
1809         CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location;   /* default */
1810         /* CurrentSMSMessage->MemoryType is input argument */
1811         CurrentSMSMessage->Type=GST_UN;                                 /* default, detection of EMPTY SMSes! */
1812         CurrentSMSMessage->Status=GSS_SENTREAD;                         /* default */
1813         CurrentSMSMessage->Class=1;                                     /* default */
1814         CurrentSMSMessage->EightBit=false;                              /* default */
1815         CurrentSMSMessage->Compression=false;                           /* default */
1816         /* CurrentSMSMessage->Location is input argument */
1817         CurrentSMSMessage->ReplyViaSameSMSC=false;                      /* default */
1818
1819         /* We may now read "unread" SMS which will change its status to "read".
1820          * We don't know its current state so we don't know whether the number
1821          * of "unread" SMSes will change.
1822          */
1823         if (!ATHW_HaveSiemensMGR)
1824                 ATHW_CNMI_count=-1;
1825
1826         ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,
1827                 (ATHW_HaveSiemensMGR ? &ATHW_RX_Patrol_SiemensMGR_struct : &ATHW_RX_Patrol_CMGR_struct)/*patrol*/,
1828                 (ATHW_HaveSiemensMGR ? "AT^SMGR=%d\r" : "AT+CMGR=%d\r"),
1829                 CurrentSMSMessage->Location);
1830         if (GE_NONE!=wait_on(&CurrentSMSMessageError,60/*timeout*/)) {
1831                 if (ATHW_CMS_INVALID_MEMORY_INDEX!=ATHW_RX_Patrol_CMS_ERROR_code)
1832                         return(CurrentSMSMessageError);
1833                 return(GE_EMPTYSMSLOCATION);
1834                 }
1835         if (CurrentSMSMessage->Type==GST_UN)    /* Empty slot with "\nOK\n" response detection */
1836                 return(GE_EMPTYSMSLOCATION);
1837
1838         return(GE_NONE);
1839 }
1840
1841 static GSM_Error ATHW_DeleteSMS(GSM_Data *data, GSM_Statemachine *state)
1842 {
1843         CurrentSMSMessage = data->SMSMessage;
1844
1845         ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage));         /* select <mem1> - reading/deleting */
1846
1847         ATHW_CNMI_count=-1;
1848
1849         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,30/*timeout*/,NULL/*patrol*/,
1850                 "AT+CMGD=%d\r",CurrentSMSMessage->Location);
1851
1852         return(GE_NONE);
1853 }
1854
1855 static const struct ATHW_RX_Patrol *ATHW_RX_Patrol_GT_SPACE_patrol_after;
1856
1857 /* RETURNS: "true" if successful
1858  */
1859 static bool WRITEPHONE_hex(unsigned char *buf,size_t buflen)
1860 {
1861 char *hex;
1862 size_t hexsize;         /* should be ==2*buflen */
1863 size_t got;
1864
1865         if (!(hex=malloc(2*buflen)))
1866                 return(false);
1867         hexsize=(SMS_BlockToHex(hex,buf,buflen)-hex);
1868         got=WRITEPHONE(PortFD,hex,hexsize);
1869         free(hex);
1870
1871         if (hexsize!=got)
1872                 return(false);
1873         return(true);
1874 }
1875
1876 static char *ATHW_RX_Patrol_GT_SPACE(char *after)
1877 {
1878         switch (ATHW_CurrentCMGF) {
1879
1880         case 0:
1881                 if (!WRITEPHONE_hex(CurrentSMSMessagePDU,CurrentSMSMessagePDU_size))
1882                         goto fail;
1883                 break;
1884
1885         case 1:
1886                 if (CurrentSMSMessage->UDHPresent) {    /* ATHW_CMGS_CMGF1_8bit_HaveBinHex==true assumed */
1887                         if (!WRITEPHONE_hex(CurrentSMSMessage->UDH,1+CurrentSMSMessage->UDH[0]))
1888                                 goto fail;
1889                         }
1890                 if (CurrentSMSMessage->EightBit && ATHW_CMGS_CMGF1_8bit_HaveBinHex) {
1891                         if (!WRITEPHONE_hex(   CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength))
1892                                 goto fail;
1893                         }
1894                 else {
1895                         if (CurrentSMSMessage->MessageTextLength!=WRITEPHONE(PortFD,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength))
1896                                 goto fail;
1897                         }
1898                 break;
1899         }
1900
1901         if (1  !=WRITEPHONE(PortFD,"\x1A"/*CTRL-Z*/,1)) {
1902 fail:
1903                 CurrentSMSMessageError=GE_INTERNALERROR;
1904                 /* FALLTHRU to exit path */
1905                 }
1906
1907         /* We may be actually doing +CMGS instead of +CMGW but we do not yet
1908          * need to patrol any output from +CMGS so don't bother its distinguishing now.
1909          */
1910         ATHW_RX_Patrol_Current=ATHW_RX_Patrol_GT_SPACE_patrol_after;
1911
1912         /* We just cannot return "after" as it would mean "eat line".
1913          * Just leaving there an additional '\n' is pretty harmless.
1914          */
1915         after[-1]='\n';
1916         return(after-1);
1917 }
1918
1919 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_GT_SPACE_struct=
1920         { "\n> ",ATHW_RX_Patrol_GT_SPACE };
1921
1922 static char *ATHW_SMS_TypeToSenderOrDestination(GSM_SMSMessage *SMS)
1923 {
1924         switch (CurrentSMSMessage->Type) {
1925                 case GST_MO:
1926                         return(SMS->Destination);
1927                 case GST_MT:
1928                         return(SMS->Sender);
1929                 default:
1930                         return(NULL);
1931                 }
1932         /* NOTREACHED */
1933 }
1934
1935 /* The second argument is the size of the data in octets,
1936  * excluding User Data Header - important only for 8bit data
1937  * Ooops, we cannot share the PDU generating code with Gnokii FBUS code
1938  * as FBUS doesn't use PDU format in its SMS-Send packet!
1939  */
1940 static GSM_Error ATHW_SendSMS_CMGF0_core(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt) G_GNUC_PRINTF(3,0);
1941 static GSM_Error ATHW_SendSMS_CMGF0_core(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt)
1942 {
1943 unsigned char pdu[GNOKII_MAX_PDU_LENGTH];
1944 unsigned char *d,*pdustart,*bodystart,*pdudatalengthp;
1945 int i;
1946 char *SMSsenderdestination;
1947
1948         d=pdu;
1949
1950         if (ATHW_CurrentSMSCPrefix) {
1951
1952                 /* We should get SMSC number */
1953                 if (CurrentSMSMessage->MessageCenter.No) {
1954 GSM_Data data_messagecenter;
1955
1956                         data_messagecenter.MessageCenter=&CurrentSMSMessage->MessageCenter;
1957                         ATHW_ERR_WRAPPER(ATHW_GetSMSCenter(&data_messagecenter,state));
1958                         CurrentSMSMessage->MessageCenter.No = 0;
1959                         }
1960                 if (!*CurrentSMSMessage->MessageCenter.Number)
1961                         *d++=0x00;      /* total SMSC length */
1962                 else {
1963                         i=SemiOctetPack(CurrentSMSMessage->MessageCenter.Number,d+1);
1964                         *d=               1/*type*/ + (i+1)/2/*rounded-up bytes of nibbles*/ ;
1965                         d+= 1/*length*/ + 1/*type*/ + (i+1)/2/*rounded-up bytes of nibbles*/ ;
1966                         }
1967                 }
1968         /* We MUST NOT count SMSCenter length into +CMGS=%d count argument
1969          */
1970         pdustart=d;
1971
1972         *d++=ATHW_SMStoFO(CurrentSMSMessage);   /* <fo>: First Octet of SMS-SUBMIT */
1973         *d++=ATHW_PDU_MR_DEFAULT;               /* Message Reference */
1974         SMSsenderdestination=ATHW_SMS_TypeToSenderOrDestination(CurrentSMSMessage);
1975         i=SemiOctetPack((!SMSsenderdestination ? "" : CurrentSMSMessage->Destination),d+1);
1976         *d= i/*nibbles*/ ;
1977         d+= 1/*length*/ + 1/*type*/ + (i+1)/2/*rounded-up bytes of nibbles*/ ;
1978         *d++=(unsigned char)CurrentSMSMessage->MessageCenter.Format;
1979         *d++=ATHW_SMStoDCS(CurrentSMSMessage);
1980         *d++=SMS_Validity_to_VP(CurrentSMSMessage->Validity);
1981         pdudatalengthp=d++;
1982
1983         bodystart=d;
1984         if (CurrentSMSMessage->UDHPresent) {
1985 size_t UDHlen=1+CurrentSMSMessage->UDH[0];
1986
1987                 memcpy(d,CurrentSMSMessage->UDH,UDHlen);
1988                 d+=UDHlen;
1989                 }
1990         if (CurrentSMSMessage->EightBit) {
1991                 memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
1992                 d+=CurrentSMSMessage->MessageTextLength;
1993         } else {
1994 size_t byteslen=PackSevenBitsToEight(
1995                         /* check it out yourself, really the number of used bits for UDH header on the start
1996                          * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
1997                          */
1998                         (7-(d-bodystart))%7,
1999                         CurrentSMSMessage->MessageText,d);
2000                 d+=byteslen;
2001         }
2002         *pdudatalengthp=((d-bodystart)*8)/(CurrentSMSMessage->EightBit ? 8 : 7);
2003
2004         CurrentSMSMessagePDU=pdu;
2005         CurrentSMSMessagePDU_size=d-pdu;
2006
2007         ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
2008         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,150/*timeout*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
2009                 commandfmt,d-pdustart);
2010         ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_post());
2011
2012         return(GE_NONE);
2013 }
2014
2015 /* Houston, we have a problem.
2016  * Siemens M20 supports +CMGW <stat> specification but on my model it just
2017  * reports ERROR (and <stat> is not respected).
2018  * Fortunately it will write "+CMGW: <index>\n" before and the message gets written
2019  * so we try to ignore ERROR reports during initial probing of <stat> support.
2020  */
2021 static bool ATHW_SaveSMS_StatSupported=true;
2022 static bool ATHW_SaveSMS_StatSupported_force=false;
2023 static int  ATHW_SaveSMS_StatSupported_retries=ATHW_SAVESMS_STATSUPPORTED_RETRIES;
2024
2025 static GSM_Error ATHW_SaveSMS_StatSupported_solve(GSM_Error err,int retried)
2026 {
2027         if (ATHW_SaveSMS_StatSupported_force)
2028                 return(err);
2029         if (err==GE_NONE || CurrentSMSMessage->MessageNumber) {
2030                 ATHW_SaveSMS_StatSupported_force=true;
2031                 return(GE_NONE);
2032                 }
2033         if (!ATHW_SaveSMS_StatSupported_retries) {
2034                 ATHW_SaveSMS_StatSupported=false;
2035                 ATHW_SaveSMS_StatSupported_force=true;
2036                 return(err);
2037                 }
2038         /* Count only the sending sessions, not each attempt.
2039          * Otherwise SMS with not-acceptable for write would
2040          * cost us ATHW_CURRENTSMSCPREFIX_RETRIES number of retries
2041          * in ATHW_SaveSMS_StatSupported_retries counter!
2042          */
2043         if (!retried)
2044                 ATHW_SaveSMS_StatSupported_retries--;
2045         return(err);
2046 }
2047
2048 /* This is just a wrapper for ATHW_CurrentSMSCPrefix retrying, please see the comment
2049  * at the definition of this variable.
2050  */
2051 /* This is just a variant of ATHW_RX_Patrol_CMGR_CMGF0(), I was too lazy to generalize it
2052  */
2053 static GSM_Error ATHW_SMS_CMGF0(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt)
2054 {
2055 GSM_Error err,err_first=GE_INTERNALERROR/*shut up GCC*/;
2056 int retry;
2057
2058         for (retry=0;retry<ATHW_CURRENTSMSCPREFIX_RETRIES;retry++) {
2059                 if (GE_NONE==(err=ATHW_SaveSMS_StatSupported_solve(ATHW_SendSMS_CMGF0_core(data,state,commandfmt),retry))) {
2060                         ATHW_CurrentSMSCPrefix_force=true;
2061                         return(err);
2062                         }
2063                 if (ATHW_CurrentSMSCPrefix_force)
2064                         return(err);
2065                 if (!retry)
2066                         err_first=err;
2067                 ATHW_CurrentSMSCPrefix=!ATHW_CurrentSMSCPrefix;
2068                 }
2069         /* Return rather the first error code as it is more probable that
2070          * it was generated with valid ATHW_CurrentSMSCPrefix setting
2071          */
2072         return(err_first);
2073 }
2074
2075 static GSM_Error ATHW_SendSMS_CMGF0(GSM_Data *data, GSM_Statemachine *state)
2076 {
2077 GSM_Error err=ATHW_SMS_CMGF0(data,state,"AT+CMGS=%d\r");
2078
2079         if (err==GE_NONE)
2080                 return(GE_SMSSENDOK);
2081         return(err);
2082 }
2083
2084 /* The second argument is the size of the data in octets,
2085    excluding User Data Header - important only for 8bit data */
2086 static GSM_Error ATHW_SendSMS_CMGF1(GSM_Data *data, GSM_Statemachine *state)
2087 {
2088         ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
2089         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,150/*timeout*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
2090                 "AT+CMGS=%s\r",ATHW_Enquote(CurrentSMSMessage->Destination));
2091         ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_post());
2092
2093         return(GE_SMSSENDOK);
2094 }
2095
2096 static char *ATHW_RX_Patrol_CMGS_CMGW(char *after)
2097 {
2098 long l;
2099
2100         l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
2101         if (l>=0 && l<=INT_MAX/* SMS->Location is int */)
2102                 CurrentSMSMessage->MessageNumber=l;
2103
2104         return(after);  /* eat line */
2105 }
2106
2107 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGS_struct=
2108         { "\n+CMGS:", ATHW_RX_Patrol_CMGS_CMGW };
2109
2110 static GSM_Error ATHW_SendSMS(GSM_Data *data, GSM_Statemachine *state)
2111 {
2112         CurrentSMSMessage = data->SMSMessage;
2113         ATHW_RX_Patrol_GT_SPACE_patrol_after=&ATHW_RX_Patrol_CMGS_struct;
2114         CurrentSMSMessage->MessageNumber=0;     /* default */
2115
2116         switch (ATHW_CurrentCMGF) {
2117                 case 0:  return(ATHW_SendSMS_CMGF0(data,state));
2118                 case 1:  return(ATHW_SendSMS_CMGF1(data,state));
2119                 }
2120         return(GE_INTERNALERROR);
2121 }
2122
2123 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGW_struct=
2124         { "\n+CMGW:", ATHW_RX_Patrol_CMGS_CMGW };
2125
2126 static GSM_Error ATHW_SaveSMS_CMGF0(GSM_Data *data, GSM_Statemachine *state)
2127 {
2128 const char *SMSstat;
2129 char *commandfmt=NULL;  /* just paranoia */
2130 GSM_Error err;
2131
2132         SMSstat=(!ATHW_SaveSMS_StatSupported ? NULL : ATHW_SMStoStat(CurrentSMSMessage));
2133         gasprintf(&commandfmt,"AT+CMGW=%%d,%s\r",(!SMSstat ? "" : ATHW_Enquote(SMSstat)));
2134         if (!commandfmt)
2135                 return(GE_INTERNALERROR);
2136
2137         err=ATHW_SMS_CMGF0(data,state,commandfmt);
2138         free(commandfmt);
2139
2140         return(err);
2141 }
2142
2143 static GSM_Error ATHW_SaveSMS_CMGF1(GSM_Data *data, GSM_Statemachine *state)
2144 {
2145 const char *SMSstat;
2146 char *SMSsenderdestination;
2147 GSM_Error err;
2148
2149         SMSstat=(!ATHW_SaveSMS_StatSupported ? NULL : ATHW_SMStoStat(CurrentSMSMessage));
2150         SMSsenderdestination=ATHW_SMS_TypeToSenderOrDestination(CurrentSMSMessage);
2151
2152         ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
2153         ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
2154                 "AT+CMGW=%s,,%s\r",
2155                 (!SMSsenderdestination ? "" : ATHW_Enquote(SMSsenderdestination)),
2156                 (!SMSstat ? "" : ATHW_Enquote(SMSstat)));
2157         err=wait_on(&CurrentSMSMessageError,150/*timeout*/);
2158         ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_post());
2159
2160         return(ATHW_SaveSMS_StatSupported_solve(err,0/*retried-no retry session here*/));
2161 }
2162
2163 static GSM_Error ATHW_SaveSMS(GSM_Data *data, GSM_Statemachine *state)
2164 {
2165         CurrentSMSMessage = data->SMSMessage;
2166         ATHW_RX_Patrol_GT_SPACE_patrol_after=&ATHW_RX_Patrol_CMGW_struct;
2167         CurrentSMSMessage->MessageNumber=0;             /* default */
2168
2169         ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage));         /* select <mem1> - writing */
2170
2171         switch (ATHW_CurrentCMGF) {
2172                 case 0:  return(ATHW_SaveSMS_CMGF0(data,state));
2173                 case 1:  return(ATHW_SaveSMS_CMGF1(data,state));
2174                 }
2175         return(GE_INTERNALERROR);
2176 }
2177
2178 static GSM_Error ATHW_Reset(GSM_Data *data, GSM_Statemachine *state)
2179 {
2180         ATHW_ERR_WRAPPER(ATHW_PhoneSetup());
2181
2182         return(GE_NONE);
2183 }
2184
2185 /* +COPS:
2186  *      CurrentNetworkInfo->NetworkCode = 3rd arg <oper>
2187  */
2188 static char *ATHW_RX_Patrol_COPS(char *after)
2189 {
2190 long l;
2191 char *s;
2192
2193         l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
2194         /* check whether numeric operator mode is on */
2195         if (l!=2)
2196                 return(after);  /* eat line */
2197
2198         ATHW_EXTRACTSTRING(CurrentNetworkInfo->NetworkCode,after/*src*/,2/*element_no*/);
2199         if (strlen(CurrentNetworkInfo->NetworkCode)!=5) {
2200 fail:
2201                 CurrentNetworkInfo->NetworkCode[0]='\0';
2202                 return(after);  /* eat line */
2203                 }
2204         for (s=CurrentNetworkInfo->NetworkCode;*s;s++)
2205                 if (!isdigit(*s))
2206                         goto fail;
2207         /* network code valid */
2208
2209         memmove(CurrentNetworkInfo->NetworkCode+3+1,CurrentNetworkInfo->NetworkCode+3,2 +1/*terminating '\0'*/);
2210         CurrentNetworkInfo->NetworkCode[3]=' ';
2211
2212         return(after);  /* eat line */
2213 }
2214
2215 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_COPS_struct=
2216         { "\n+COPS:", ATHW_RX_Patrol_COPS };
2217
2218 static void ATHW_RX_Patrol_CREG_hex4valid(char *hex4)
2219 {
2220 char *s;
2221
2222         if (strlen(hex4)!=4) {
2223 fail:
2224                 hex4[0]='\0';
2225                 return;
2226                 }
2227         for (s=hex4;*s;s++)
2228                 if (!isxdigit(*s))
2229                         goto fail;
2230 }
2231
2232 /* +CREG:
2233  *      CurrentNetworkInfo->CellID = 4rd arg <lac>
2234  *      CurrentNetworkInfo->LAC    = 3nd arg <cid>
2235  */
2236 static char *ATHW_RX_Patrol_CREG(char *after)
2237 {
2238 long l;
2239
2240         l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
2241         /* check whether we don't report stale information
2242          */
2243         if (l==1/*registered-home network*/ || l==5/*registered-roaming*/)
2244                 return(after);  /* eat line */
2245
2246         ATHW_EXTRACTSTRING(           CurrentNetworkInfo->CellID,after/*src*/,3/*element_no*/);
2247         ATHW_RX_Patrol_CREG_hex4valid(CurrentNetworkInfo->CellID);
2248         ATHW_EXTRACTSTRING(           CurrentNetworkInfo->LAC   ,after/*src*/,2/*element_no*/);
2249         ATHW_RX_Patrol_CREG_hex4valid(CurrentNetworkInfo->LAC   );
2250
2251         return(after);  /* eat line */
2252 }
2253
2254 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CREG_struct=
2255         { "\n+CREG:", ATHW_RX_Patrol_CREG };
2256
2257 static GSM_Error ATHW_GetNetworkInfo(GSM_Data *data, GSM_Statemachine *state)
2258 {
2259         CurrentNetworkInfo=data->NetworkInfo;
2260
2261         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentNetworkInfoError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_COPS_struct/*patrol*/,
2262                 "AT+COPS?\r");
2263         ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentNetworkInfoError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CREG_struct/*patrol*/,
2264                 "AT+CREG?\r");
2265
2266         /* When no information was gather we will rather report failure
2267          */
2268         if (1
2269                 && !*CurrentNetworkInfo->NetworkCode
2270                 && !*CurrentNetworkInfo->CellID
2271                 && !*CurrentNetworkInfo->LAC
2272                 )
2273                 return(GE_INTERNALERROR);
2274         
2275
2276         return(GE_NONE);
2277 }
2278
2279
2280 #ifndef WIN32
2281
2282 /* Called by initialisation code to open comm port in asynchronous mode. */
2283
2284 static bool ATHW_OpenSerial(void)
2285 {
2286         int result;
2287   
2288 #if __unices__
2289         int rtn;
2290 #else
2291         struct sigaction sig_io;
2292
2293         /* Set up and install handler before enabling async IO on port. */
2294
2295         sig_io.sa_handler = ATHW_SigHandler;
2296         sig_io.sa_flags = 0;
2297         sigaction (SIGIO, &sig_io, NULL);
2298 #endif
2299
2300         /* Open device. */
2301
2302         result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, GCT_Serial);
2303
2304         if (!result) {
2305                 perror(_("Couldn't open AT device"));
2306                 return false;
2307         }
2308
2309 #if __unices__
2310         /* create a thread to handle incoming data from mobile phone */
2311         rtn = pthread_create(&selThread, NULL, (void*)ATHW_SelectLoop, (void*)NULL);
2312         if (rtn != 0) return false;
2313 #endif
2314
2315         /* device_changespeed() not needed as device_open() now automatically
2316          * sets the user-specified (or default) speed.
2317          */
2318         return (true);
2319 }
2320
2321 static void ATHW_SigHandler(int status)
2322 {
2323         unsigned char buffer[255];
2324         int count, res;
2325         res = device_read(buffer, 255);
2326         for (count = 0; count < res ; count ++)
2327                 ATHW_RX_Char(buffer[count]);
2328 }
2329 #endif /* WIN32 */
2330
2331 static char *ATHW_RX_Patrol_OK(char *after)
2332 {
2333         /* Some patrol may have already indicated some error!
2334          */
2335         if (ATHW_CatchBufferErrorP && *ATHW_CatchBufferErrorP==GE_BUSY)
2336                 *ATHW_CatchBufferErrorP=GE_NONE;
2337
2338         return(after);  /* eat line */
2339 }
2340
2341 static char *ATHW_RX_Patrol_ERROR(char *after)
2342 {
2343         if (ATHW_CatchBufferErrorP)
2344                 *ATHW_CatchBufferErrorP=GE_INTERNALERROR;
2345
2346         return(after);  /* eat line */
2347 }
2348
2349 /* We can't kick the user with anything else */
2350 #define ATHW_RX_Patrol_NO_CARRIER  ATHW_RX_Patrol_ERROR
2351 #define ATHW_RX_Patrol_DELAYED     ATHW_RX_Patrol_ERROR
2352 #define ATHW_RX_Patrol_NO_DIALTONE ATHW_RX_Patrol_ERROR
2353 #define ATHW_RX_Patrol_BUSY        ATHW_RX_Patrol_ERROR
2354
2355 static char *ATHW_RX_Patrol_CMX_ERROR(char *after,long *errorp)
2356 {
2357 char *end=NULL;
2358 long l;
2359
2360         while (*after==' ') after++;
2361
2362         l=strtol(after,&end,10);
2363         if (*after && l>=0 && l<LONG_MAX && end && *end=='\n')
2364                 *errorp=l;
2365
2366         if (ATHW_CatchBufferErrorP)
2367                 *ATHW_CatchBufferErrorP=GE_INTERNALERROR;
2368
2369         return(after);  /* eat line */
2370 }
2371
2372 static char *ATHW_RX_Patrol_CME_ERROR(char *after)
2373 {
2374         return(ATHW_RX_Patrol_CMX_ERROR(after,&ATHW_RX_Patrol_CME_ERROR_code));
2375 }
2376
2377 static void ATHW_RX_Patrol_CME_ERROR_reset(void)
2378 {
2379         ATHW_RX_Patrol_CME_ERROR_code=-1;
2380 }
2381
2382 static char *ATHW_RX_Patrol_CMS_ERROR(char *after)
2383 {
2384         return(ATHW_RX_Patrol_CMX_ERROR(after,&ATHW_RX_Patrol_CMS_ERROR_code));
2385 }
2386
2387 static void ATHW_RX_Patrol_CMS_ERROR_reset(void)
2388 {
2389         ATHW_RX_Patrol_CMS_ERROR_code=-1;
2390 }
2391
2392 static char *ATHW_RX_Patrol_CRING_VOICE(char *after)
2393 {
2394         if (CallPassup)
2395                 (*CallPassup)('V');
2396
2397         return(after);  /* eat line */
2398 }
2399
2400 static char *ATHW_RX_Patrol_CMTI(char *after)
2401 {
2402 #ifdef ATHW_DEBUG
2403         printf("ATHW_RX_Patrol_CMTI: ATHW_CNMI_count==%d\n",ATHW_CNMI_count);
2404 #endif
2405         
2406         if (ATHW_CNMI_count>=0) {
2407                 ATHW_CNMI_count++;
2408 #ifdef ATHW_DEBUG
2409                 printf("ATHW_CNMI_count increased to %d\n",ATHW_CNMI_count);
2410 #endif
2411                 }
2412
2413         return(after);  /* eat line */
2414 }
2415
2416 static const struct ATHW_RX_Patrol ATHW_RX_Patrols[]={
2417         { "\nOK\n"           ,ATHW_RX_Patrol_OK },
2418         { "\nERROR\n"        ,ATHW_RX_Patrol_ERROR },
2419         { "\nNO CARRIER\n"   ,ATHW_RX_Patrol_NO_CARRIER },
2420         { "\nDELAYED\n"      ,ATHW_RX_Patrol_DELAYED },
2421         { "\nNO DIALTONE\n"  ,ATHW_RX_Patrol_NO_DIALTONE },
2422         { "\nBUSY\n"         ,ATHW_RX_Patrol_BUSY },
2423         { "\n+CME ERROR:"    ,ATHW_RX_Patrol_CME_ERROR, ATHW_RX_Patrol_CME_ERROR_reset },
2424         { "\n+CMS ERROR:"    ,ATHW_RX_Patrol_CMS_ERROR, ATHW_RX_Patrol_CMS_ERROR_reset },
2425         { "\n+CRING: VOICE\n",ATHW_RX_Patrol_CRING_VOICE },
2426         { "\n+CMTI:"         ,ATHW_RX_Patrol_CMTI },
2427         };
2428
2429 static void ATHW_CatchBufferReset(void)
2430 {
2431         ATHW_CatchBufferPtr=ATHW_CatchBuffer;
2432         *ATHW_CatchBufferPtr='\0';
2433         ATHW_CatchBufferMarker=NULL;
2434 }
2435
2436 static void ATHW_CatchBufferMarkStart(GSM_Error *errorcodep)
2437 {
2438 static GSM_Error err_trashcan;
2439 const struct ATHW_RX_Patrol *patrol;
2440
2441         if (!errorcodep)
2442                 errorcodep=&err_trashcan;
2443         ATHW_CatchBufferErrorP=errorcodep;
2444
2445         ATHW_CatchBufferMarker=ATHW_CatchBufferPtr;
2446
2447         for (patrol=ATHW_RX_Patrols;patrol<ATHW_RX_Patrols+ARRAY_LEN(ATHW_RX_Patrols);patrol++)
2448                 if (patrol->reset)
2449                         (*patrol->reset)();
2450 }
2451
2452 static const struct ATHW_RX_Patrol *ATHW_RX_Char_EvalPatrol_best;
2453 static char                        *ATHW_RX_Char_EvalPatrol_best_found;
2454
2455 static void ATHW_RX_Char_EvalPatrol(const struct ATHW_RX_Patrol *patrol)
2456 {
2457 char *found,*foundend;
2458
2459         if (!patrol)
2460                 return;
2461
2462         if (!(found=strstr(ATHW_CatchBuffer,patrol->buoy)))
2463                 return;
2464
2465         /* When the buoy doesn't end with '\n' we need to find some '\n' after it.
2466          * Otherwise the whole line hasn't been read yet and we to yet wait.
2467          * It means that we doesn't support Patrol which would catch an incomplete line
2468          *  - the only exception are the explicite checks for functions like "ATHW_RX_Patrol_GT_SPACE_struct" :-(
2469          */
2470         foundend=found+strlen(patrol->buoy);
2471         if (foundend<=found)            /* assert */
2472                 return;
2473         if (1   /* FIXME: This list is an ugly solution... */
2474                 && patrol!=&ATHW_RX_Patrol_GT_SPACE_struct
2475                 && patrol!=&ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct
2476                 && patrol!=&ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct
2477
2478                         && foundend>found && foundend[-1]!='\n' && !strchr(foundend,'\n'))
2479                 return;         /* no whole line has been read yet */
2480
2481         if (!ATHW_RX_Char_EvalPatrol_best || ATHW_RX_Char_EvalPatrol_best_found>found) {
2482                 ATHW_RX_Char_EvalPatrol_best=patrol;
2483                 ATHW_RX_Char_EvalPatrol_best_found=found;
2484                 }
2485 }
2486
2487
2488 /* RX_State machine for receive handling.  Called once for each character
2489    received from the phone/phone. */
2490
2491 static void ATHW_RX_Char(char rx_byte)
2492 {
2493 size_t offset;
2494 #ifdef ATHW_DEBUG
2495 #if 0
2496         dprintf(_("Received character '%c' (0x%02X)\n"),
2497                 (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
2498 #endif
2499         putchar(rx_byte);
2500 #endif
2501
2502 /* We try to keep back 1/2 of buffer data, ineffectively shifting it up when the buffer
2503  * gets filled up.
2504  */
2505         if (ATHW_CatchBufferPtr>=ATHW_CatchBuffer+sizeof(ATHW_CatchBuffer) -1/*Terminating '\0'*/ ) {
2506 char *movefrom;
2507
2508 #ifdef ATHW_DEBUG
2509                 dprintf(_("Shifting buffer:\n%s\n__END__\n"),ATHW_CatchBuffer);
2510 #endif
2511                 movefrom=ATHW_CatchBuffer+(sizeof(ATHW_CatchBuffer)/2);
2512                 if (1           && ATHW_CatchBufferMarker
2513                                 && ATHW_CatchBufferMarker>ATHW_CatchBuffer
2514                                 && ATHW_CatchBufferMarker<movefrom)
2515                         movefrom=ATHW_CatchBufferMarker;
2516                 offset=ATHW_CatchBuffer+sizeof(ATHW_CatchBuffer)-movefrom;
2517                 memmove(ATHW_CatchBuffer,movefrom,offset);
2518                 ATHW_CatchBufferPtr-=offset;
2519                 if (ATHW_CatchBufferMarker) {
2520                         if ((ATHW_CatchBufferMarker-=offset)<ATHW_CatchBuffer)
2521                                 ATHW_CatchBufferMarker=ATHW_CatchBuffer;
2522                         }
2523                 }
2524
2525         /* NEVER store '\0' as it would knock-out completely our patrolling system!
2526          */
2527         if (rx_byte=='\r' || rx_byte=='\0')
2528                 rx_byte='\n';
2529
2530         *ATHW_CatchBufferPtr++=rx_byte;
2531         *ATHW_CatchBufferPtr='\0';
2532
2533         for (;;) {
2534 const struct ATHW_RX_Patrol *patrol;
2535 char *found,*end,*foundend;
2536
2537                 ATHW_RX_Char_EvalPatrol_best=NULL;
2538                 for (patrol=ATHW_RX_Patrols;patrol<ATHW_RX_Patrols+ARRAY_LEN(ATHW_RX_Patrols);patrol++) {
2539                         ATHW_RX_Char_EvalPatrol(patrol);
2540                         }
2541                 ATHW_RX_Char_EvalPatrol(ATHW_RX_Patrol_Current);
2542                 if (!ATHW_RX_Char_EvalPatrol_best)
2543                         return;
2544
2545 #if 0
2546 #ifdef ATHW_DEBUG
2547                 printf("Invoking patrol for: %s\n",ATHW_RX_Char_EvalPatrol_best->buoy);
2548 #endif
2549 #endif
2550                 found=ATHW_RX_Char_EvalPatrol_best_found;
2551                 foundend=found+strlen(ATHW_RX_Char_EvalPatrol_best->buoy);
2552                 end=(*ATHW_RX_Char_EvalPatrol_best->func)(foundend);
2553
2554                 if (!end)               /* patrol returned NULL - it is on the track but it needs more data! */
2555                         return;
2556                 if (end==foundend) {    /* they did simple 'return(after);' */
2557                         if (foundend[-1]!='\n') {
2558                                 if (!(end=strchr(foundend,'\n')))
2559                                         end=foundend+strlen(foundend);
2560                                 }
2561                         }
2562                 /* We place '\n' delimited at the *end */
2563                 memmove(found+1,end,ATHW_CatchBufferPtr+1-end);
2564                 offset=end-(found+1);
2565                 ATHW_CatchBufferPtr-=offset;
2566                 ATHW_CatchBufferPtr[-1]='\n';
2567
2568                 /* Move Marker right behind the squeezed data if we were inside
2569                  */
2570                 if (ATHW_CatchBufferMarker>=found) {
2571                         if (ATHW_CatchBufferMarker<=end)
2572                                 ATHW_CatchBufferMarker=ATHW_CatchBufferPtr+1;
2573                         else
2574                                 ATHW_CatchBufferMarker-=offset;
2575                         }
2576                 }
2577 }
2578
2579 /* Here we initialise model specific functions. */
2580
2581 #define ATHW_FUNCTIONS_ENTRY(name) \
2582         case GOP_##name:        return(ATHW_##name(data,state));
2583
2584 static GSM_Error ATHW_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
2585 {
2586 static GSM_Statemachine *catcher=NULL;
2587
2588         if (!catcher || catcher==state)
2589                 catcher=state;
2590         else
2591                 *((char *)NULL)=1;
2592
2593         switch (op) {
2594                 ATHW_FUNCTIONS_ENTRY(Init)
2595                 ATHW_FUNCTIONS_ENTRY(Terminate)
2596                 ATHW_FUNCTIONS_ENTRY(GetMemoryStatus)
2597                 ATHW_FUNCTIONS_ENTRY(ReadPhonebook)
2598                 ATHW_FUNCTIONS_ENTRY(WritePhonebook)
2599                 ATHW_FUNCTIONS_ENTRY(GetSMSStatus)
2600                 ATHW_FUNCTIONS_ENTRY(GetSMSCenter)
2601                 ATHW_FUNCTIONS_ENTRY(SetSMSCenter)
2602                 ATHW_FUNCTIONS_ENTRY(GetSMS)
2603                 ATHW_FUNCTIONS_ENTRY(DeleteSMS)
2604                 ATHW_FUNCTIONS_ENTRY(SendSMS)
2605                 ATHW_FUNCTIONS_ENTRY(SaveSMS)
2606                 ATHW_FUNCTIONS_ENTRY(GetRFLevel)
2607                 ATHW_FUNCTIONS_ENTRY(GetBatteryLevel)
2608                 ATHW_FUNCTIONS_ENTRY(GetPowersource)
2609                 ATHW_FUNCTIONS_ENTRY(GetImei)
2610                 ATHW_FUNCTIONS_ENTRY(GetRevision)
2611                 ATHW_FUNCTIONS_ENTRY(GetModel)
2612                 ATHW_FUNCTIONS_ENTRY(GetManufacturer)
2613                 ATHW_FUNCTIONS_ENTRY(DialVoice)
2614                 ATHW_FUNCTIONS_ENTRY(DialData)
2615                 ATHW_FUNCTIONS_ENTRY(GetIncomingCallNr)
2616                 ATHW_FUNCTIONS_ENTRY(Reset)
2617                 ATHW_FUNCTIONS_ENTRY(CancelCall)
2618                 ATHW_FUNCTIONS_ENTRY(AnswerCall)
2619                 ATHW_FUNCTIONS_ENTRY(GetNetworkInfo)
2620
2621                 case GOP_Identify: {
2622 GSM_Error err,r=GE_NONE;
2623                         if (GE_NONE!=(err=ATHW_GetImei        (data,state)))
2624                                 r=err;
2625                         if (GE_NONE!=(err=ATHW_GetRevision    (data,state)))
2626                                 r=err;
2627                         if (GE_NONE!=(err=ATHW_GetModel       (data,state)))
2628                                 r=err;
2629                         if (GE_NONE!=(err=ATHW_GetManufacturer(data,state)))
2630                                 r=err;
2631                         return(r);
2632                         }
2633
2634                 default:
2635                         return(GE_NOTIMPLEMENTED);
2636         }
2637 }
2638
2639 GSM_Phone phone_at_hw = {
2640         UNIMPLEMENTED,  /* IncomingFunctions - we don't use default StateMachine */
2641         UNIMPLEMENTED,  /* DefaultFunction   - we don't use default StateMachine */
2642         /* Mobile phone information */
2643         {
2644                 "AT",                   /* Supported models */
2645                 31,                     /* Max RF Level (AT+CSQ) */
2646                 0,                      /* Min RF Level (AT+CSQ) */
2647                 GRF_CSQ,                /* RF level units */
2648                 100,                    /* Max Battery Level (AT+CBC) */
2649                 0,                      /* Min Battery Level (AT+CBC) */
2650                 GBU_Percentage,         /* Battery level units */
2651                 GDT_None,               /* Have date/time support */
2652                 GDT_None,               /* Alarm supports time only */
2653                 0,                      /* No alarm available */
2654                 48, 84,                 /* Startup logo size */
2655                 14, 72,                 /* Op logo size */
2656                 14, 72,                 /* Caller logo size */
2657         },
2658         ATHW_Functions,
2659 };