7 A Linux/Unix toolset and driver for Nokia mobile phones.
9 Copyright (C) 2001 Jan Kratochvil,
10 based on code by Hugh Blemings & Pavel JanÃk ml.
12 Released under the terms of the GNU GPL, see file COPYING for more details.
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.
18 The various routines are called ATHW (whatever) as a concatenation of AT
19 and HardWare communication type.
22 Revision 1.1.1.1 2002/04/03 00:08:02 short
23 Found in "gnokii-working" directory, some November-patches version
29 /* #define DISABLE_CMGF0 1 */ /* Force AT+CMGF=1 on phones capable +CMGF=0 */
31 /* System header files */
43 #include "win32/winserial.h"
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))
55 #define WRITEPHONE(a, b, c) device_write(b, c)
61 #include <sys/ioctl.h>
62 #include <sys/types.h>
67 #include "devices/unixserial.h"
71 /* Various header file */
75 #include "gsm-common.h"
77 /* Global variables used by code in gsm-api.c to expose the functions
78 supported by this model of phone. */
82 /* fd opened in device.c */
83 extern int device_portfd;
86 /* Our private defines */
88 #define ATHW_CME_NOT_FOUND (22) /* +CME ERROR: 22 */
89 #define ATHW_CMS_INVALID_MEMORY_INDEX (321) /* +CMS ERROR: 321 */
91 /* When now catchbuffer was provided and must have some space to decode
92 * OK/ERROR/... result codes.
94 #define ATHW_CATCHBUFFER_LENGTH 0x400
96 /* Default message reference, filled-in by GSM modem
98 #define ATHW_PDU_MR_DEFAULT (0x00)
100 /* Maximum PDU size in bytes
102 #define GNOKII_MAX_PDU_LENGTH (64+GSM_MAX_DESTINATION_LENGTH/2+(GSM_MAX_SMS_LENGTH*7)/8)
104 /* Local variables */
108 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
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 */
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
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
133 #define ATHW_CURRENTSMSCPREFIX_RETRIES (4)
135 /* Please see the comment above ATHW_SaveSMS_StatSupported_solve().
136 * Number of retry sessions before giving up sending SMS <stat> during SaveSMS.
138 #define ATHW_SAVESMS_STATSUPPORTED_RETRIES (2)
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;
146 static long ATHW_RX_Patrol_CME_ERROR_code;
147 static long ATHW_RX_Patrol_CMS_ERROR_code;
149 typedef char *(*ATHW_RX_PatrolFunc)(char *after);
150 typedef void (*ATHW_RX_PatrolReset)(void);
152 struct ATHW_RX_Patrol {
154 ATHW_RX_PatrolFunc func;
155 ATHW_RX_PatrolReset reset;
158 static const struct ATHW_RX_Patrol *ATHW_RX_Patrol_Current;
160 static void ATHW_CatchBufferReset(void);
161 static void ATHW_CatchBufferMarkStart(GSM_Error *errorcodep);
165 static pthread_t Thread;
167 static pthread_t selThread;
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. */
177 static GSM_MemoryStatus *CurrentMemoryStatus;
178 static GSM_Error CurrentMemoryStatusError;
180 static GSM_PhonebookEntry *CurrentPhonebookEntry;
181 static GSM_Error CurrentPhonebookError;
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;
188 static GSM_SMSStatus *CurrentSMSStatus;
189 static GSM_Error CurrentSMSStatusError;
191 static GSM_MessageCenter *CurrentMessageCenter;
192 static GSM_Error CurrentMessageCenterError;
194 static float *CurrentRFLevel; /* AT+CSQ */
195 static GSM_Error GetRFLevelError;
197 static float *CurrentBatteryLevel; /* AT+CBC */
198 static GSM_Error GetBatteryLevelError;
200 static GSM_PowerSource *CurrentPowersource; /* AT+CBC */
201 static GSM_Error GetPowersourceError;
203 static GSM_Error DialVoiceError;
205 static GSM_Error CancelCallError;
207 static GSM_Error CurrentPhoneInfoError;
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];
214 static char CurrentIncomingCall[20] = " ";
216 static GSM_NetworkInfo *CurrentNetworkInfo;
217 static GSM_Error CurrentNetworkInfoError;
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);
225 /* "catchbufer" can be provided as NULL:
226 * - sizeof() will be bogus but it will be ingored anyway
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,...)
239 ATHW_RX_Patrol_Current=patrol;
242 command_len=gvasprintf(&command,fmt,ap);
244 if (-1==command_len) {
246 *errorcodep=GE_INTERNALERROR;
250 ATHW_CatchBufferMarkStart(errorcodep);
252 writephone_got=WRITEPHONE(PortFD, command, command_len);
256 write(1,command,command_len);
261 if (command_len!=writephone_got) {
263 *errorcodep=GE_INTERNALERROR;
267 /* success but we don't wait for the result code */
270 /* This function is used to get storage status from the phone. It currently
271 supports two different memory areas - internal and SIM. */
274 wait_on(volatile GSM_Error *what, int timeout)
276 GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
278 while (timeout && ((r=*what)==GE_BUSY)) {
285 /* any specific patrollers are no longer valid */
286 ATHW_RX_Patrol_Current=NULL;
289 printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
295 #define WAIT_ON(what, timeout) \
297 GSM_Error res = wait_on(what, timeout); \
298 if (res != GE_NONE) \
302 /* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
304 #define ATHW_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
306 GSM_Error _ATHW_TX_SENDCOMMAND_WAIT_ON_err,*_ATHW_TX_SENDCOMMAND_WAIT_ON_errp; \
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)); \
315 #define ATHW_ERR_WRAPPER(expr) \
317 GSM_Error err=(expr); \
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.
327 static void ATHW_ExtractString(char *dest,size_t destlen,char *src,int element_no)
329 char *srcend,*start,*end;
332 if (!(srcend=strchr(src,'\n')))
333 return; /* INTERNAL error! */
335 while (*src==' ') src++;
338 while (src<srcend && *src!=',') src++;
341 else { /* *src=='"' */
343 while (src<srcend && !(src[0]=='"' && (src[1]==',' || src+1==srcend))) src++;
348 while (*src==' ') src++;
351 /* here we have <start..end) as the current element */
352 } while (element_no--);
354 if (destlen-1<end-start)
357 memcpy(dest,start,end-start);
358 dest[end-start]='\0';
361 #define ATHW_EXTRACTSTRING(dest,src,element_no) (ATHW_ExtractString((dest),sizeof((dest)),(src),(element_no)))
363 static long ATHW_ExtractNumber(char *src,int element_no)
365 char buf[32],*end=NULL;
368 ATHW_EXTRACTSTRING(buf,src,element_no);
369 r=strtol(buf,&end,10);
370 if (!*buf || (end && *end))
375 /* s/^\s+//; s/\s+$//; s/\s+/ /g; */
377 static void ATHW_BufferTrimCopy(char *d_buf,size_t d_len,const char *s)
379 char *d_end=d_buf+d_len,*d;
381 for (d=d_buf;*s && d<d_end -1/*Terminating '\0'*/ ;s++) {
383 if (d==d_buf || d[-1]==' ')
390 if (d>d_buf && d[-1]==' ')
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))
398 static GSM_Error ATHW_GetPhoneInfo(void)
400 /* +CGMI=Request Manufacturer Identification
401 * +CGMM=Request Model Identification
402 * +CGMR=Request Revision Identification
403 * +CGSN=Request Product Serial Number Identification (IMEI)
406 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMI\r");
407 ATHW_BUFFERTRIMCOPY(Manufacturer,ATHW_CatchBufferMarker);
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);
413 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGMR\r");
414 ATHW_BUFFERTRIMCOPY(Revision,ATHW_CatchBufferMarker);
416 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"/AT+CGSN\r");
417 ATHW_BUFFERTRIMCOPY(IMEI,ATHW_CatchBufferMarker);
422 static unsigned char ATHW_SMStoFO(GSM_SMSMessage *SMS)
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*/
433 static unsigned char ATHW_SMStoDCS(GSM_SMSMessage *SMS)
435 int class=(SMS->Class==-1 ? 1 : SMS->Class); /* we default to class 1 (Mobile Equipment target) */
437 if (!SMS->EightBit && class==1)
439 return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
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.)
445 static GSM_Error ATHW_SMS_CMGF01_pre(void)
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);
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),
458 /* FIXME: Should we query current "MessageCenter.No" to get "Format" field? */
459 /* <pid>: */ (unsigned char)CurrentSMSMessage->MessageCenter.Format,
461 /* <dcs>: */ ATHW_SMStoDCS(CurrentSMSMessage)
465 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
471 static GSM_Error ATHW_SMS_CMGF01_post(void)
473 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
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?
482 static const char *ATHW_Enquote(const char *string)
485 #define ATHW_ENQUOTE_SLOTS (4)
487 struct ATHW_Enquote_slot {
492 static struct ATHW_Enquote_slot slots[ATHW_ENQUOTE_SLOTS];
493 static struct ATHW_Enquote_slot *slot=slots;
494 size_t stringl=strlen(string);
498 if (slot->len<1+stringl+1+1) { /* "string"\0 */
500 size_t newlen=(!slot->len ? 0x100 : slot->len*2);
502 if (!(newbuf=realloc(slot->buf,newlen)))
510 for (s=string;*s;s++) {
519 if (++slot>=slots+ARRAY_LEN(slots))
524 static char *ATHW_PhoneSetup_CMGF1_Detect_Patrol_9(char *after)
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().
533 ATHW_CMGS_CMGF1_8bit_HaveBinHex=true;
535 WRITEPHONE(PortFD,"Q",1);
537 /* We don't need to patrol for "Q", its detection would be cross-phone
541 return(after); /* eat line - it will be just one character... */
544 static const struct ATHW_RX_Patrol ATHW_PhoneSetup_CMGF1_Detect_Patrol_9_struct=
545 { "9",ATHW_PhoneSetup_CMGF1_Detect_Patrol_9 };
547 static char *ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE(char *after)
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... */
554 static const struct ATHW_RX_Patrol ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE_struct=
555 { "\n> ",ATHW_PhoneSetup_CMGF1_Detect_Patrol_GT_SPACE };
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.
562 static GSM_Error ATHW_PhoneSetup_CMGF1_Detect_core(void)
566 ATHW_CMGS_CMGF1_8bit_HaveBinHex=false; /* default */
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.
573 ATHW_CatchBufferReset();
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();
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*/));
594 /* Timeout 5 is too small for Nokia 9000i, give it a rest...
596 if (GE_TIMEOUT==wait_on(&CurrentSMSMessageError,20/*timeout*/))
597 ATHW_CMGS_CMGF1_8bit_HaveBinHex=false;
602 static GSM_Error ATHW_PhoneSetup_CMGS_Reset(void)
607 /* We try to escape AT+CMGS mode, at least Siemens M20 then needs to get some rest
609 WRITEPHONE(PortFD,"\x1B\r",2);
612 ATHW_CatchBufferReset(); /* output can be very bogus due to escaping */
614 for (retry=0;retry<3;retry++) {
615 ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
617 if (wait_on(&err,10/*timeout*/)==GE_NONE)
618 return(err); /* success */
620 /* Hmm, we've probably stucked the phone :-(
622 return(err); /* failure */
625 static void ATHW_PhoneSetup_CMGF1_Detect(void)
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 */
631 printf("ATHW_PhoneSetup_CMGF1_Detect: ATHW_CMGS_CMGF1_8bit_HaveBinHex=%d\n",(int)ATHW_CMGS_CMGF1_8bit_HaveBinHex);
635 static GSM_Error ATHW_PhoneSetup_CMGFSet(int CMGF)
638 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
639 "AT+CMGF=%d\r",CMGF);
640 ATHW_CurrentCMGF=CMGF;
643 ATHW_PhoneSetup_CMGF1_Detect();
648 static GSM_Error ATHW_PhoneSetup(void)
652 ATHW_CurrentMemoryType=GMT_XX; /* invalidate */
654 ATHW_PhoneSetup_CMGS_Reset();
656 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,
659 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
661 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
663 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
665 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
666 "AT+CRC=1\r"); /* request "+CRING: VOICE" reporting */
668 /* Try to detect Siemens M20 with M1 backward compatibility mode */
669 ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
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;
675 ATHW_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
676 "AT+CSMS=0\r"); /* turn on ATHW_CurrentSMSCPrefix, if possible */
678 #ifndef DISABLE_CMGF0
679 if (GE_NONE!=ATHW_PhoneSetup_CMGFSet(0))
681 ATHW_PhoneSetup_CMGFSet(1);
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*/));
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*/))
699 ATHW_CNMI_count=-1; /* unknown yet */
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 */
705 /* Enable LAC+CellID returns, it will also disable unsolicited changes reporting
706 * but we ignore it successfuly.
708 ATHW_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
710 wait_on(&err,10/*timeout*/); /* error ignored, GetNetworkInfo will find the failure itself */
718 static void ATHW_RX_Char(char rx_byte);
719 static void ATHW_SigHandler(int status);
720 static bool ATHW_OpenSerial(void);
722 GSM_Phone phone_at_hw; /* forward declaration */
724 /* Initialise variables and state machine. */
726 static GSM_Error ATHW_Init(GSM_Data *data, GSM_Statemachine *state)
728 RequestTerminate = false;
731 /* Create and start main thread. */
736 rtn = ! OpenConnection(State->Link.PortDevice,ATHW_RX_Char,ATHW_KeepAliveProc);
738 return(GE_INTERNALERROR);
741 SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
742 if (!ATHW_OpenSerial())
743 return(GE_INTERNALERROR);
746 ATHW_ERR_WRAPPER(ATHW_PhoneSetup());
752 /* thread for handling incoming data */
753 void ATHW_SelectLoop()
757 struct timeval timeout;
760 FD_SET(device_portfd, &readfds);
761 /* set timeout to 15 seconds */
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");
773 static GSM_Error ATHW_AnswerCall(GSM_Data *data, GSM_Statemachine *state)
775 /* data->CallNo not present up to Nokia 9110i */
776 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentPhoneInfoError/*errorcodep*/,20/*timeout*/,NULL/*patrol*/,"ATA\r");
780 /* Applications should call ATHW_Terminate to shut down the ATHW thread and
781 close the serial port. */
783 static GSM_Error ATHW_Terminate(GSM_Data *data, GSM_Statemachine *state)
785 /* Request termination of thread */
786 RequestTerminate = true;
789 /* Now wait for thread to terminate. */
790 pthread_join(Thread, NULL);
792 /* Close serial port. */
802 /* +CSQ: Signal Quality
803 * CurrentRFLevel = 1st arg <rssi>
805 static char *ATHW_RX_Patrol_CSQ(char *after)
809 l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
813 return(after); /* eat line */
816 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSQ_struct=
817 { "\n+CSQ:", ATHW_RX_Patrol_CSQ };
819 static GSM_Error ATHW_GetRFLevel(GSM_Data *data, GSM_Statemachine *state)
821 CurrentRFLevel=data->RFLevel;
823 ATHW_TX_SENDCOMMAND_WAIT_ON(&GetRFLevelError/*errorcodep*/,30/*timeout*/,&ATHW_RX_Patrol_CSQ_struct,"AT+CSQ\r");
825 if (*data->RFUnits!=GRF_CSQ) {
826 *data->RFUnits = GRF_Percentage; /* required by xgnokii_lowlevel.c */
827 *data->RFLevel = (*data->RFLevel)*100/31; /* +CSQ */
833 /* +CBC: Battery Charge
834 * CurrentPowersource = 1st arg <bcs>
835 * CurrentBatteryLevel = 2nd arg <bcl>
837 static char *ATHW_RX_Patrol_CBC(char *after)
841 if (CurrentPowersource) {
842 l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
844 *CurrentPowersource=(l==1 ? GPS_ACDC : GPS_BATTERY);
847 if (CurrentBatteryLevel) {
848 l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
850 *CurrentBatteryLevel=l;
853 return(after); /* eat line */
856 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CBC_struct=
857 { "\n+CBC:", ATHW_RX_Patrol_CBC };
859 static GSM_Error ATHW_GetBatteryLevel(GSM_Data *data, GSM_Statemachine *state)
861 CurrentBatteryLevel=data->BatteryLevel;
862 *data->BatteryUnits = GBU_Percentage;
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
868 *CurrentBatteryLevel=100;
875 static GSM_Error ATHW_GetPowersource(GSM_Data *data, GSM_Statemachine *state)
877 CurrentPowersource=data->PowerSource;
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
883 *CurrentPowersource=GPS_ACDC;
889 static GSM_Error ATHW_DialVoice(GSM_Data *data, GSM_Statemachine *state)
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!
894 ATHW_TX_SENDCOMMAND_WAIT_ON(&DialVoiceError/*errorcodep*/,600/*timeout*/,NULL/*patrol*/,"ATD%s;\r",data->VoiceNumber);
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?
906 static GSM_Error ATHW_DialData(GSM_Data *data, GSM_Statemachine *state)
908 CallPassup=data->DataCallPassUp;
910 switch (*data->DataType) {
911 case 0: /* FALLTHRU */
912 case 1: /* FALLTHRU */
916 case -1: /* Just used to set the call passup */
921 ATHW_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,"ATD%s\r",data->DataNumber);
925 static GSM_Error ATHW_GetIncomingCallNr(GSM_Data *data, GSM_Statemachine *state)
927 if (*CurrentIncomingCall != ' ') {
928 strcpy(data->IncomingCallNr, CurrentIncomingCall);
930 } else return GE_BUSY;
933 static GSM_Error ATHW_CancelCall(GSM_Data *data, GSM_Statemachine *state)
935 ATHW_TX_SENDCOMMAND_WAIT_ON(&CancelCallError/*errorcodep*/,60/*timeout*/,NULL/*patrol*/,"ATH\r");
940 /* messagecenter->No" is NOT set as it is input argument */
941 static void ATHW_MessageCenterClear(GSM_MessageCenter *messagecenter)
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 */
951 * MessageCenter->Number = 1st arg <sca>
953 static char *ATHW_RX_Patrol_CSCA(char *after)
955 ATHW_EXTRACTSTRING(CurrentMessageCenter->Number,after/*src*/,0/*element_no*/);
957 return(after); /* eat line */
960 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSCA_struct=
961 { "\n+CSCA:", ATHW_RX_Patrol_CSCA };
964 * CurrentMessageCenter->Format = 3rd arg <pid>
965 * CurrentMessageCenter->Validity = 2nd arg <vp>
967 static char *ATHW_RX_Patrol_CSMP(char *after)
971 l=ATHW_ExtractNumber(after/*src*/,2/*element_no*/);
973 CurrentMessageCenter->Format=(GSM_SMSMessageFormat)l;
975 l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
977 CurrentMessageCenter->Validity=(GSM_SMSMessageValidity)l;
979 return(after); /* eat line */
982 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CSMP_struct=
983 { "\n+CSMP:", ATHW_RX_Patrol_CSMP };
985 /* This function sends to the mobile phone a request for the SMS Center */
987 static GSM_Error ATHW_GetSMSCenter(GSM_Data *data, GSM_Statemachine *state)
989 CurrentMessageCenter = data->MessageCenter;
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);
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");
998 printf("message center OK: %s\n",CurrentMessageCenter->Number);
1002 /* This function set the SMS Center profile on the phone. */
1004 static GSM_Error ATHW_SetSMSCenter(GSM_Data *data, GSM_Statemachine *state)
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));
1012 static int ATHW_RX_Patrol_SiemensMGL_count;
1014 static char *ATHW_RX_Patrol_SiemensMGL(char *after)
1018 /* There may be left string from AT+CMGL=? which would confuse us!
1020 for (s=after;isspace(*s);s++);
1022 return(after); /* eat line */
1024 /* Lines with our wanted "REC UNREAD" should be already filtered by the request
1026 ATHW_RX_Patrol_SiemensMGL_count++;
1028 return(after); /* eat line */
1031 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_SiemensMGL_struct=
1032 { "\n^SMGL:", ATHW_RX_Patrol_SiemensMGL };
1034 static void ATHW_Update_CNMI_count(void)
1038 if (!ATHW_HaveSiemensMGL)
1041 switch (ATHW_CurrentCMGF) {
1042 case 0: state="0"; break;
1043 case 1: state=ATHW_Enquote("REC UNREAD"); break;
1045 return; /* not supported */
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 */
1056 * CurrentSMSStatus->Used = 2nd arg <used1>
1057 * CurrentSMSStatus->Slots = 3nd arg <total1>
1059 static char *ATHW_RX_Patrol_CPMS(char *after)
1063 l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
1064 if (l>=0 && l<INT_MAX)
1065 CurrentSMSStatus->Used=l;
1067 l=ATHW_ExtractNumber(after/*src*/,2/*element_no*/);
1068 if (l>=0 && l<INT_MAX)
1069 CurrentSMSStatus->Slots=l;
1071 return(after); /* eat line */
1074 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPMS_struct=
1075 { "\n+CPMS:", ATHW_RX_Patrol_CPMS };
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"
1081 static GSM_Error ATHW_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
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,
1094 static GSM_Error ATHW_GetImei(GSM_Data *data, GSM_Statemachine *state)
1097 strcpy(data->Imei,IMEI);
1099 } else return (GE_TRYAGAIN);
1102 static GSM_Error ATHW_GetRevision(GSM_Data *data, GSM_Statemachine *state)
1105 strcpy(data->Revision,Revision);
1107 } else return (GE_TRYAGAIN);
1110 static GSM_Error ATHW_GetModel(GSM_Data *data, GSM_Statemachine *state)
1113 strcpy(data->Model,Model);
1115 } else return (GE_TRYAGAIN);
1118 static GSM_Error ATHW_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
1121 strcpy(data->Manufacturer,Manufacturer);
1123 } else return (GE_TRYAGAIN);
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")
1131 #define GETMEMORYTYPE_ENTRY(code) \
1132 case GMT_##code: return( #code );
1134 static const char *ATHW_GetMemoryType(GSM_MemoryType memory_type)
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);
1153 #define ATHW_GETMEMORYTYPE(dst,memory_type) \
1155 (dst)=ATHW_GetMemoryType((memory_type)); \
1157 return(GE_INVALIDMEMORYTYPE); \
1160 static GSM_Error ATHW_SelectPhonebookMemory(GSM_MemoryType memory_type)
1162 const char *atmemtype;
1164 if (memory_type==ATHW_CurrentMemoryType)
1166 ATHW_CurrentMemoryType=GMT_XX;
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));
1172 ATHW_CurrentMemoryType=memory_type;
1177 /* +CPBR: (GetMemoryStatus)
1178 * CurrentMemoryStatus->Free=1st arg (1-<index>)
1180 static char *ATHW_RX_Patrol_CPBR_GetMemoryStatus(char *after)
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;
1191 return(after); /* eat line */
1194 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPBR_GetMemoryStatus_struct=
1195 { "\n+CPBR:", ATHW_RX_Patrol_CPBR_GetMemoryStatus };
1197 static GSM_Error ATHW_GetMemoryStatus(GSM_Data *data, GSM_Statemachine *state)
1199 CurrentMemoryStatus=data->MemoryStatus;
1201 ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentMemoryStatus->MemoryType));
1203 CurrentMemoryStatus->Used=0; /* default */
1204 CurrentMemoryStatus->Free=0; /* default */
1206 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentMemoryStatusError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CPBR_GetMemoryStatus_struct,
1212 static void ATHW_DateTimeSetCurrent(GSM_DateTime *datetime)
1214 time_t current=time(NULL);
1215 struct tm *tm=localtime(¤t);
1217 datetime->AlarmEnabled=false;
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;
1226 datetime->Timezone=timezone;
1229 /* +CPBR: (ReadPhonebook)
1230 * CurrentPhonebookEntry->Name =4th arg <text>
1231 * CurrentPhonebookEntry->Number=2nd arg <number>
1233 static char *ATHW_RX_Patrol_CPBR_ReadPhonebook(char *after)
1235 CurrentPhonebookEntry->Empty=false;
1236 ATHW_EXTRACTSTRING(CurrentPhonebookEntry->Name ,after/*src*/,3/*element_no*/);
1237 ATHW_EXTRACTSTRING(CurrentPhonebookEntry->Number,after/*src*/,1/*element_no*/);
1239 return(after); /* eat line */
1242 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CPBR_ReadPhonebook_struct=
1243 { "\n+CPBR:", ATHW_RX_Patrol_CPBR_ReadPhonebook };
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
1249 static GSM_Error ATHW_ReadPhonebook(GSM_Data *data, GSM_Statemachine *state)
1251 CurrentPhonebookEntry=data->PhonebookEntry;
1253 ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentPhonebookEntry->MemoryType));
1255 /* We may get just OK response (Siemens M20) which means empty location
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 */
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
1269 if (ATHW_CME_NOT_FOUND!=ATHW_RX_Patrol_CME_ERROR_code)
1270 return(CurrentPhonebookError);
1278 /* Routine to write phonebook location in phone. Designed to be called by
1279 application code. Will block until location is written or timeout
1282 static GSM_Error ATHW_WritePhonebook(GSM_Data *data, GSM_Statemachine *state)
1284 CurrentPhonebookEntry=data->PhonebookEntry;
1286 ATHW_ERR_WRAPPER(ATHW_SelectPhonebookMemory(CurrentPhonebookEntry->MemoryType));
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)
1298 static const char *ATHW_SMStoStat(GSM_SMSMessage *SMS)
1300 switch (CurrentSMSMessage->Type) {
1302 switch (CurrentSMSMessage->Status) {
1303 case GSS_NOTSENTREAD:
1304 return("REC UNREAD");
1310 switch (CurrentSMSMessage->Status) {
1311 case GSS_NOTSENTREAD:
1312 return("STO UNSENT");
1323 /* RETURN: Success of recognizing "SMSstat"
1325 static bool ATHW_StattoSMS(GSM_SMSMessage *SMS,const char *SMSstat)
1327 /**/ if (!strcmp(SMSstat,"0") || !strcmp(SMSstat,"REC UNREAD")) {
1329 SMS->Status=GSS_NOTSENTREAD;
1331 else if (!strcmp(SMSstat,"1") || !strcmp(SMSstat,"REC READ")) {
1333 SMS->Status=GSS_SENTREAD;
1335 else if (!strcmp(SMSstat,"2") || !strcmp(SMSstat,"STO UNSENT")) {
1337 SMS->Status=GSS_NOTSENTREAD;
1339 else if (!strcmp(SMSstat,"3") || !strcmp(SMSstat,"STO SENT")) {
1341 SMS->Status=GSS_SENTREAD;
1343 else return(false); /* not recognized! */
1347 /* scts=="01/10/25,02:28:18+08", scts WILL BE DESTROYED!
1349 static bool ATHW_SCTStoSMS_CMGF1(GSM_SMSMessage *SMS,char *scts)
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! */
1356 if (strlen(scts)!=20)
1358 if ((timezone_minus=(scts[17]=='-')))
1362 if (!isdigit(scts[i++]))
1364 if (!isdigit(scts[i++]))
1366 if (scts[i]!=fmt[i])
1369 /* string is completely valid now */
1378 &DateTime->Timezone);
1379 if (DateTime->Year>=85)
1380 DateTime->Year-=100;
1381 DateTime->Year+=2000;
1383 DateTime->Timezone=-DateTime->Timezone;
1387 static void ATHW_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
1389 switch ((dcs&0xF0)>>4) {
1393 CurrentSMSMessage->EightBit=false;
1398 CurrentSMSMessage->EightBit=!!(dcs&0x04); /* bit 2 */
1399 CurrentSMSMessage->Class=(dcs&0x03); /* bits 0 & 1 */
1404 static bool ATHW_SCTStoSMS_CMGF0(GSM_SMSMessage *SMS,unsigned char *scts)
1406 GSM_DateTime *DateTime=&SMS->Time;
1407 int *fields[]={ /* FIXME: offsetof() would be nice here but currently not supported by Gnokii */
1414 &DateTime->Timezone,
1417 unsigned char digit0,digit1;
1419 for (i=0;i<ARRAY_LEN(fields);i++,scts++) {
1421 || (digit0= (*scts) &0x0F) > 9
1422 || (digit1=((*scts)>>4)&0x0F) > 9
1425 (*fields[i])=10*digit0 + digit1;
1427 /* scts is completely valid now */
1429 if (DateTime->Year>=85)
1430 DateTime->Year-=100;
1431 DateTime->Year+=2000;
1435 /* Value MUST match ((fo>>3)&0x03) !
1437 enum ATHW_FO_Validity {
1439 ATHW_FOV_Reserved=1,
1440 ATHW_FOV_Relative=2,
1441 ATHW_FOV_Absolute=3,
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 */
1450 /* RETURN: Success of recognizing "SMSstat"
1452 static void ATHW_FOtoSMS(GSM_SMSMessage *SMS,enum ATHW_FO_Validity *validityp,unsigned char fo)
1455 case 0: SMS->Type=GST_MT; break;
1456 case 1: SMS->Type=GST_MO; break;
1457 default: /* FIXME: value 2 and 3? */
1460 /* bit 2 - reject duplices - not supported by Gnokii
1464 *validityp=(enum ATHW_FO_Validity)((fo>>3)&0x03);
1466 /* bit 5 - status report request - not supported by Gnokii
1469 SMS->UDHPresent=!!(fo&(1<<6));
1471 SMS->ReplyViaSameSMSC=!!(fo&(1<<7));
1474 static GSM_Error ATHW_RX_Patrol_CMGR_CMGF0_core(char *after,char *messagetext,char *end)
1476 unsigned char pdu[GNOKII_MAX_PDU_LENGTH],*pduend,*s;
1478 size_t oada_size,udl;
1479 enum ATHW_FO_Validity FO_validity;
1480 unsigned udbits; /* 7 or 8 bits in UserData */
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);
1487 if (ATHW_CurrentSMSCPrefix) {
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 */
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 */
1501 if (s +1/*<fo>*/ +1/*received number length*/ > pduend)
1502 return(GE_INTERNALERROR);
1503 ATHW_FOtoSMS(CurrentSMSMessage,&FO_validity,*s++);
1505 switch (CurrentSMSMessage->Type) {
1508 oada =CurrentSMSMessage->Sender;
1509 oada_size=sizeof(CurrentSMSMessage->Sender);
1513 oada =CurrentSMSMessage->Destination;
1514 oada_size=sizeof(CurrentSMSMessage->Destination);
1516 /* message reference is really not interesting to be read */
1517 if (s +1/*<mr>*/ > pduend)
1518 return(GE_INTERNALERROR);
1523 /* FIXME: GST_DR - Delivery Report - not supported!
1526 /* Unable to skip OA/DA, parsing would fail
1528 return(GE_INTERNALERROR);
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 */
1538 if (s +1/*<pid>*/ +1/*<dcs>*/ > pduend)
1539 return(GE_INTERNALERROR);
1541 /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
1542 CurrentSMSMessage->MessageCenter.Format=*s++; /* <pid> */
1544 ATHW_DCStoSMS(CurrentSMSMessage,*s++); /* <dcs> */
1546 switch (CurrentSMSMessage->Type) {
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);
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) {
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);
1567 case ATHW_FOV_Absolute:
1568 /* leave default, not supported by Gnokii */
1571 s+=ATHW_FO_Validity_sizes[(unsigned)FO_validity];
1575 /* To be written when other types get supported in the above switch
1579 if (s +1/*udl*/ > pduend)
1580 return(GE_INTERNALERROR);
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().
1587 udbits=(CurrentSMSMessage->EightBit ? 8 : 7);
1588 if (s +( udl*udbits +7/*round-up*/ )/8 != pduend)
1589 return(GE_INTERNALERROR);
1591 if (CurrentSMSMessage->UDHPresent) {
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)
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;
1603 if (udl > (sizeof(CurrentSMSMessage->MessageText) -1/*terminating '\0'*/))
1604 return(GE_INTERNALERROR);
1605 CurrentSMSMessage->MessageTextLength=udl;
1606 CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength]='\0';
1609 memcpy(CurrentSMSMessage->MessageText,s,udl);
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 */
1616 CurrentSMSMessage->MessageText /* output */
1618 CurrentSMSMessage->MessageText[udl]='\0';
1624 /* This is just a variant of ATHW_SMS_CMGF0(), I was too lazy to generalize it
1626 static GSM_Error ATHW_RX_Patrol_CMGR_CMGF0_retrier(char *after,char *messagetext,char *end)
1628 GSM_Error err,err_first=GE_INTERNALERROR/*shut up GCC*/;
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()).
1635 if (GE_NONE==(err=ATHW_RX_Patrol_CMGR_CMGF0_core(after,messagetext,end),1/*retried*/)) {
1636 ATHW_CurrentSMSCPrefix_force=true;
1639 if (ATHW_CurrentSMSCPrefix_force)
1643 ATHW_CurrentSMSCPrefix=!ATHW_CurrentSMSCPrefix;
1645 /* Return rather the first error code as it is more probable that
1646 * it was generated with valid ATHW_CurrentSMSCPrefix setting
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.
1654 static void ATHW_RX_Patrol_CMGR_CMGF0(char *after,char *messagetext,char *end)
1658 err=ATHW_RX_Patrol_CMGR_CMGF0_retrier(after,messagetext,end);
1660 CurrentSMSMessageError=err;
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
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
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
1676 static void ATHW_RX_Patrol_CMGR_CMGF1(char *after,char *messagetext,char *end)
1679 int fieldno_fo=-1,fieldno_dcs=-1;
1682 switch (CurrentSMSMessage->Type) {
1685 ATHW_EXTRACTSTRING(CurrentSMSMessage->Sender,after/*src*/,1/*element_no*/);
1687 /* <scts> Service centre time stamp string format "01/10/25,02:28:18+08"
1688 * Note: not present up to Nokia 9110i
1690 ATHW_EXTRACTSTRING(buf ,after/*src*/,3/*element_no*/);
1691 ATHW_SCTStoSMS_CMGF1(CurrentSMSMessage,buf/*scts*/); /* error ignored, leave the default value */
1698 ATHW_EXTRACTSTRING(CurrentSMSMessage->Destination,after/*src*/,1/*element_no*/);
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);
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 */
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);
1725 ATHW_EXTRACTSTRING(CurrentSMSMessage->MessageCenter.Number,after/*src*/,8/*element_no*/);
1727 CurrentSMSMessage->MessageTextLength=GNOKII_MIN(end-messagetext,sizeof(CurrentSMSMessage->MessageText)-1);
1728 memcpy(CurrentSMSMessage->MessageText,messagetext,CurrentSMSMessage->MessageTextLength);
1729 CurrentSMSMessage->MessageText[CurrentSMSMessage->MessageTextLength]='\0';
1733 * CurrentSMSMessage->Type = 1st arg <stat>
1735 static char *ATHW_RX_Patrol_CMGR(char *after)
1737 char buf[32],*end,*messagetext;
1739 end=strchr(after,'\n');
1741 return(after); /* assert, INTERNAL! */
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 */
1747 if (!(end=strchr(end,'\n')))
1748 return(NULL); /* need data */
1749 /* Now we have read "+CMGR:*\n[\n]*\n" */
1751 /* 1st arg <stat> is numeric(+CMGF==0)/alpha(+CMGF==1), ATHW_StattoSMS parses it all
1753 ATHW_EXTRACTSTRING(buf,after/*src*/,0/*element_no*/);
1754 ATHW_StattoSMS(CurrentSMSMessage,buf); /* error ignored: what to do if unknown? */;
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;
1761 return(++end); /* eat the whole SMS block, INCLUDING the trailing '\n' */
1764 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGR_struct=
1765 { "\n+CMGR:", ATHW_RX_Patrol_CMGR };
1767 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_SiemensMGR_struct=
1768 { "\n^SMGR:", ATHW_RX_Patrol_CMGR };
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!
1777 static GSM_Error ATHW_SMS_SelectMemoryType(GSM_SMSMessage *SMS)
1779 const char *atmemtype;
1780 const char *atmemtype_quoted;
1782 ATHW_GETMEMORYTYPE(atmemtype,SMS->MemoryType);
1783 atmemtype_quoted=ATHW_Enquote(atmemtype);
1785 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
1786 "AT+CPMS=%s,%s\r",atmemtype_quoted,atmemtype_quoted);
1791 static GSM_Error ATHW_GetSMS(GSM_Data *data, GSM_Statemachine *state)
1793 CurrentSMSMessage = data->SMSMessage;
1795 ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage)); /* select <mem1> - reading/deleting */
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 */
1804 ATHW_MessageCenterClear(&CurrentSMSMessage->MessageCenter); /* default */
1805 CurrentSMSMessage->MessageCenter.No=0; /* default - input for GetSMSCenter */
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 */
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.
1823 if (!ATHW_HaveSiemensMGR)
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);
1835 if (CurrentSMSMessage->Type==GST_UN) /* Empty slot with "\nOK\n" response detection */
1836 return(GE_EMPTYSMSLOCATION);
1841 static GSM_Error ATHW_DeleteSMS(GSM_Data *data, GSM_Statemachine *state)
1843 CurrentSMSMessage = data->SMSMessage;
1845 ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage)); /* select <mem1> - reading/deleting */
1849 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSMessageError/*errorcodep*/,30/*timeout*/,NULL/*patrol*/,
1850 "AT+CMGD=%d\r",CurrentSMSMessage->Location);
1855 static const struct ATHW_RX_Patrol *ATHW_RX_Patrol_GT_SPACE_patrol_after;
1857 /* RETURNS: "true" if successful
1859 static bool WRITEPHONE_hex(unsigned char *buf,size_t buflen)
1862 size_t hexsize; /* should be ==2*buflen */
1865 if (!(hex=malloc(2*buflen)))
1867 hexsize=(SMS_BlockToHex(hex,buf,buflen)-hex);
1868 got=WRITEPHONE(PortFD,hex,hexsize);
1876 static char *ATHW_RX_Patrol_GT_SPACE(char *after)
1878 switch (ATHW_CurrentCMGF) {
1881 if (!WRITEPHONE_hex(CurrentSMSMessagePDU,CurrentSMSMessagePDU_size))
1886 if (CurrentSMSMessage->UDHPresent) { /* ATHW_CMGS_CMGF1_8bit_HaveBinHex==true assumed */
1887 if (!WRITEPHONE_hex(CurrentSMSMessage->UDH,1+CurrentSMSMessage->UDH[0]))
1890 if (CurrentSMSMessage->EightBit && ATHW_CMGS_CMGF1_8bit_HaveBinHex) {
1891 if (!WRITEPHONE_hex( CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength))
1895 if (CurrentSMSMessage->MessageTextLength!=WRITEPHONE(PortFD,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength))
1901 if (1 !=WRITEPHONE(PortFD,"\x1A"/*CTRL-Z*/,1)) {
1903 CurrentSMSMessageError=GE_INTERNALERROR;
1904 /* FALLTHRU to exit path */
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.
1910 ATHW_RX_Patrol_Current=ATHW_RX_Patrol_GT_SPACE_patrol_after;
1912 /* We just cannot return "after" as it would mean "eat line".
1913 * Just leaving there an additional '\n' is pretty harmless.
1919 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_GT_SPACE_struct=
1920 { "\n> ",ATHW_RX_Patrol_GT_SPACE };
1922 static char *ATHW_SMS_TypeToSenderOrDestination(GSM_SMSMessage *SMS)
1924 switch (CurrentSMSMessage->Type) {
1926 return(SMS->Destination);
1928 return(SMS->Sender);
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!
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)
1943 unsigned char pdu[GNOKII_MAX_PDU_LENGTH];
1944 unsigned char *d,*pdustart,*bodystart,*pdudatalengthp;
1946 char *SMSsenderdestination;
1950 if (ATHW_CurrentSMSCPrefix) {
1952 /* We should get SMSC number */
1953 if (CurrentSMSMessage->MessageCenter.No) {
1954 GSM_Data data_messagecenter;
1956 data_messagecenter.MessageCenter=&CurrentSMSMessage->MessageCenter;
1957 ATHW_ERR_WRAPPER(ATHW_GetSMSCenter(&data_messagecenter,state));
1958 CurrentSMSMessage->MessageCenter.No = 0;
1960 if (!*CurrentSMSMessage->MessageCenter.Number)
1961 *d++=0x00; /* total SMSC length */
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*/ ;
1968 /* We MUST NOT count SMSCenter length into +CMGS=%d count argument
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);
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);
1984 if (CurrentSMSMessage->UDHPresent) {
1985 size_t UDHlen=1+CurrentSMSMessage->UDH[0];
1987 memcpy(d,CurrentSMSMessage->UDH,UDHlen);
1990 if (CurrentSMSMessage->EightBit) {
1991 memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
1992 d+=CurrentSMSMessage->MessageTextLength;
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
1998 (7-(d-bodystart))%7,
1999 CurrentSMSMessage->MessageText,d);
2002 *pdudatalengthp=((d-bodystart)*8)/(CurrentSMSMessage->EightBit ? 8 : 7);
2004 CurrentSMSMessagePDU=pdu;
2005 CurrentSMSMessagePDU_size=d-pdu;
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());
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.
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;
2025 static GSM_Error ATHW_SaveSMS_StatSupported_solve(GSM_Error err,int retried)
2027 if (ATHW_SaveSMS_StatSupported_force)
2029 if (err==GE_NONE || CurrentSMSMessage->MessageNumber) {
2030 ATHW_SaveSMS_StatSupported_force=true;
2033 if (!ATHW_SaveSMS_StatSupported_retries) {
2034 ATHW_SaveSMS_StatSupported=false;
2035 ATHW_SaveSMS_StatSupported_force=true;
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!
2044 ATHW_SaveSMS_StatSupported_retries--;
2048 /* This is just a wrapper for ATHW_CurrentSMSCPrefix retrying, please see the comment
2049 * at the definition of this variable.
2051 /* This is just a variant of ATHW_RX_Patrol_CMGR_CMGF0(), I was too lazy to generalize it
2053 static GSM_Error ATHW_SMS_CMGF0(GSM_Data *data, GSM_Statemachine *state,const char *commandfmt)
2055 GSM_Error err,err_first=GE_INTERNALERROR/*shut up GCC*/;
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;
2063 if (ATHW_CurrentSMSCPrefix_force)
2067 ATHW_CurrentSMSCPrefix=!ATHW_CurrentSMSCPrefix;
2069 /* Return rather the first error code as it is more probable that
2070 * it was generated with valid ATHW_CurrentSMSCPrefix setting
2075 static GSM_Error ATHW_SendSMS_CMGF0(GSM_Data *data, GSM_Statemachine *state)
2077 GSM_Error err=ATHW_SMS_CMGF0(data,state,"AT+CMGS=%d\r");
2080 return(GE_SMSSENDOK);
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)
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());
2093 return(GE_SMSSENDOK);
2096 static char *ATHW_RX_Patrol_CMGS_CMGW(char *after)
2100 l=ATHW_ExtractNumber(after/*src*/,0/*element_no*/);
2101 if (l>=0 && l<=INT_MAX/* SMS->Location is int */)
2102 CurrentSMSMessage->MessageNumber=l;
2104 return(after); /* eat line */
2107 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGS_struct=
2108 { "\n+CMGS:", ATHW_RX_Patrol_CMGS_CMGW };
2110 static GSM_Error ATHW_SendSMS(GSM_Data *data, GSM_Statemachine *state)
2112 CurrentSMSMessage = data->SMSMessage;
2113 ATHW_RX_Patrol_GT_SPACE_patrol_after=&ATHW_RX_Patrol_CMGS_struct;
2114 CurrentSMSMessage->MessageNumber=0; /* default */
2116 switch (ATHW_CurrentCMGF) {
2117 case 0: return(ATHW_SendSMS_CMGF0(data,state));
2118 case 1: return(ATHW_SendSMS_CMGF1(data,state));
2120 return(GE_INTERNALERROR);
2123 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CMGW_struct=
2124 { "\n+CMGW:", ATHW_RX_Patrol_CMGS_CMGW };
2126 static GSM_Error ATHW_SaveSMS_CMGF0(GSM_Data *data, GSM_Statemachine *state)
2128 const char *SMSstat;
2129 char *commandfmt=NULL; /* just paranoia */
2132 SMSstat=(!ATHW_SaveSMS_StatSupported ? NULL : ATHW_SMStoStat(CurrentSMSMessage));
2133 gasprintf(&commandfmt,"AT+CMGW=%%d,%s\r",(!SMSstat ? "" : ATHW_Enquote(SMSstat)));
2135 return(GE_INTERNALERROR);
2137 err=ATHW_SMS_CMGF0(data,state,commandfmt);
2143 static GSM_Error ATHW_SaveSMS_CMGF1(GSM_Data *data, GSM_Statemachine *state)
2145 const char *SMSstat;
2146 char *SMSsenderdestination;
2149 SMSstat=(!ATHW_SaveSMS_StatSupported ? NULL : ATHW_SMStoStat(CurrentSMSMessage));
2150 SMSsenderdestination=ATHW_SMS_TypeToSenderOrDestination(CurrentSMSMessage);
2152 ATHW_ERR_WRAPPER(ATHW_SMS_CMGF01_pre());
2153 ATHW_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&ATHW_RX_Patrol_GT_SPACE_struct/*patrol*/,
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());
2160 return(ATHW_SaveSMS_StatSupported_solve(err,0/*retried-no retry session here*/));
2163 static GSM_Error ATHW_SaveSMS(GSM_Data *data, GSM_Statemachine *state)
2165 CurrentSMSMessage = data->SMSMessage;
2166 ATHW_RX_Patrol_GT_SPACE_patrol_after=&ATHW_RX_Patrol_CMGW_struct;
2167 CurrentSMSMessage->MessageNumber=0; /* default */
2169 ATHW_ERR_WRAPPER(ATHW_SMS_SelectMemoryType(CurrentSMSMessage)); /* select <mem1> - writing */
2171 switch (ATHW_CurrentCMGF) {
2172 case 0: return(ATHW_SaveSMS_CMGF0(data,state));
2173 case 1: return(ATHW_SaveSMS_CMGF1(data,state));
2175 return(GE_INTERNALERROR);
2178 static GSM_Error ATHW_Reset(GSM_Data *data, GSM_Statemachine *state)
2180 ATHW_ERR_WRAPPER(ATHW_PhoneSetup());
2186 * CurrentNetworkInfo->NetworkCode = 3rd arg <oper>
2188 static char *ATHW_RX_Patrol_COPS(char *after)
2193 l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
2194 /* check whether numeric operator mode is on */
2196 return(after); /* eat line */
2198 ATHW_EXTRACTSTRING(CurrentNetworkInfo->NetworkCode,after/*src*/,2/*element_no*/);
2199 if (strlen(CurrentNetworkInfo->NetworkCode)!=5) {
2201 CurrentNetworkInfo->NetworkCode[0]='\0';
2202 return(after); /* eat line */
2204 for (s=CurrentNetworkInfo->NetworkCode;*s;s++)
2207 /* network code valid */
2209 memmove(CurrentNetworkInfo->NetworkCode+3+1,CurrentNetworkInfo->NetworkCode+3,2 +1/*terminating '\0'*/);
2210 CurrentNetworkInfo->NetworkCode[3]=' ';
2212 return(after); /* eat line */
2215 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_COPS_struct=
2216 { "\n+COPS:", ATHW_RX_Patrol_COPS };
2218 static void ATHW_RX_Patrol_CREG_hex4valid(char *hex4)
2222 if (strlen(hex4)!=4) {
2233 * CurrentNetworkInfo->CellID = 4rd arg <lac>
2234 * CurrentNetworkInfo->LAC = 3nd arg <cid>
2236 static char *ATHW_RX_Patrol_CREG(char *after)
2240 l=ATHW_ExtractNumber(after/*src*/,1/*element_no*/);
2241 /* check whether we don't report stale information
2243 if (l==1/*registered-home network*/ || l==5/*registered-roaming*/)
2244 return(after); /* eat line */
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 );
2251 return(after); /* eat line */
2254 static const struct ATHW_RX_Patrol ATHW_RX_Patrol_CREG_struct=
2255 { "\n+CREG:", ATHW_RX_Patrol_CREG };
2257 static GSM_Error ATHW_GetNetworkInfo(GSM_Data *data, GSM_Statemachine *state)
2259 CurrentNetworkInfo=data->NetworkInfo;
2261 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentNetworkInfoError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_COPS_struct/*patrol*/,
2263 ATHW_TX_SENDCOMMAND_WAIT_ON(&CurrentNetworkInfoError/*errorcodep*/,10/*timeout*/,&ATHW_RX_Patrol_CREG_struct/*patrol*/,
2266 /* When no information was gather we will rather report failure
2269 && !*CurrentNetworkInfo->NetworkCode
2270 && !*CurrentNetworkInfo->CellID
2271 && !*CurrentNetworkInfo->LAC
2273 return(GE_INTERNALERROR);
2282 /* Called by initialisation code to open comm port in asynchronous mode. */
2284 static bool ATHW_OpenSerial(void)
2291 struct sigaction sig_io;
2293 /* Set up and install handler before enabling async IO on port. */
2295 sig_io.sa_handler = ATHW_SigHandler;
2296 sig_io.sa_flags = 0;
2297 sigaction (SIGIO, &sig_io, NULL);
2302 result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, GCT_Serial);
2305 perror(_("Couldn't open AT device"));
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;
2315 /* device_changespeed() not needed as device_open() now automatically
2316 * sets the user-specified (or default) speed.
2321 static void ATHW_SigHandler(int status)
2323 unsigned char buffer[255];
2325 res = device_read(buffer, 255);
2326 for (count = 0; count < res ; count ++)
2327 ATHW_RX_Char(buffer[count]);
2331 static char *ATHW_RX_Patrol_OK(char *after)
2333 /* Some patrol may have already indicated some error!
2335 if (ATHW_CatchBufferErrorP && *ATHW_CatchBufferErrorP==GE_BUSY)
2336 *ATHW_CatchBufferErrorP=GE_NONE;
2338 return(after); /* eat line */
2341 static char *ATHW_RX_Patrol_ERROR(char *after)
2343 if (ATHW_CatchBufferErrorP)
2344 *ATHW_CatchBufferErrorP=GE_INTERNALERROR;
2346 return(after); /* eat line */
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
2355 static char *ATHW_RX_Patrol_CMX_ERROR(char *after,long *errorp)
2360 while (*after==' ') after++;
2362 l=strtol(after,&end,10);
2363 if (*after && l>=0 && l<LONG_MAX && end && *end=='\n')
2366 if (ATHW_CatchBufferErrorP)
2367 *ATHW_CatchBufferErrorP=GE_INTERNALERROR;
2369 return(after); /* eat line */
2372 static char *ATHW_RX_Patrol_CME_ERROR(char *after)
2374 return(ATHW_RX_Patrol_CMX_ERROR(after,&ATHW_RX_Patrol_CME_ERROR_code));
2377 static void ATHW_RX_Patrol_CME_ERROR_reset(void)
2379 ATHW_RX_Patrol_CME_ERROR_code=-1;
2382 static char *ATHW_RX_Patrol_CMS_ERROR(char *after)
2384 return(ATHW_RX_Patrol_CMX_ERROR(after,&ATHW_RX_Patrol_CMS_ERROR_code));
2387 static void ATHW_RX_Patrol_CMS_ERROR_reset(void)
2389 ATHW_RX_Patrol_CMS_ERROR_code=-1;
2392 static char *ATHW_RX_Patrol_CRING_VOICE(char *after)
2397 return(after); /* eat line */
2400 static char *ATHW_RX_Patrol_CMTI(char *after)
2403 printf("ATHW_RX_Patrol_CMTI: ATHW_CNMI_count==%d\n",ATHW_CNMI_count);
2406 if (ATHW_CNMI_count>=0) {
2409 printf("ATHW_CNMI_count increased to %d\n",ATHW_CNMI_count);
2413 return(after); /* eat line */
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 },
2429 static void ATHW_CatchBufferReset(void)
2431 ATHW_CatchBufferPtr=ATHW_CatchBuffer;
2432 *ATHW_CatchBufferPtr='\0';
2433 ATHW_CatchBufferMarker=NULL;
2436 static void ATHW_CatchBufferMarkStart(GSM_Error *errorcodep)
2438 static GSM_Error err_trashcan;
2439 const struct ATHW_RX_Patrol *patrol;
2442 errorcodep=&err_trashcan;
2443 ATHW_CatchBufferErrorP=errorcodep;
2445 ATHW_CatchBufferMarker=ATHW_CatchBufferPtr;
2447 for (patrol=ATHW_RX_Patrols;patrol<ATHW_RX_Patrols+ARRAY_LEN(ATHW_RX_Patrols);patrol++)
2452 static const struct ATHW_RX_Patrol *ATHW_RX_Char_EvalPatrol_best;
2453 static char *ATHW_RX_Char_EvalPatrol_best_found;
2455 static void ATHW_RX_Char_EvalPatrol(const struct ATHW_RX_Patrol *patrol)
2457 char *found,*foundend;
2462 if (!(found=strstr(ATHW_CatchBuffer,patrol->buoy)))
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" :-(
2470 foundend=found+strlen(patrol->buoy);
2471 if (foundend<=found) /* assert */
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
2478 && foundend>found && foundend[-1]!='\n' && !strchr(foundend,'\n'))
2479 return; /* no whole line has been read yet */
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;
2488 /* RX_State machine for receive handling. Called once for each character
2489 received from the phone/phone. */
2491 static void ATHW_RX_Char(char rx_byte)
2496 dprintf(_("Received character '%c' (0x%02X)\n"),
2497 (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
2502 /* We try to keep back 1/2 of buffer data, ineffectively shifting it up when the buffer
2505 if (ATHW_CatchBufferPtr>=ATHW_CatchBuffer+sizeof(ATHW_CatchBuffer) -1/*Terminating '\0'*/ ) {
2509 dprintf(_("Shifting buffer:\n%s\n__END__\n"),ATHW_CatchBuffer);
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;
2525 /* NEVER store '\0' as it would knock-out completely our patrolling system!
2527 if (rx_byte=='\r' || rx_byte=='\0')
2530 *ATHW_CatchBufferPtr++=rx_byte;
2531 *ATHW_CatchBufferPtr='\0';
2534 const struct ATHW_RX_Patrol *patrol;
2535 char *found,*end,*foundend;
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);
2541 ATHW_RX_Char_EvalPatrol(ATHW_RX_Patrol_Current);
2542 if (!ATHW_RX_Char_EvalPatrol_best)
2547 printf("Invoking patrol for: %s\n",ATHW_RX_Char_EvalPatrol_best->buoy);
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);
2554 if (!end) /* patrol returned NULL - it is on the track but it needs more data! */
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);
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';
2568 /* Move Marker right behind the squeezed data if we were inside
2570 if (ATHW_CatchBufferMarker>=found) {
2571 if (ATHW_CatchBufferMarker<=end)
2572 ATHW_CatchBufferMarker=ATHW_CatchBufferPtr+1;
2574 ATHW_CatchBufferMarker-=offset;
2579 /* Here we initialise model specific functions. */
2581 #define ATHW_FUNCTIONS_ENTRY(name) \
2582 case GOP_##name: return(ATHW_##name(data,state));
2584 static GSM_Error ATHW_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
2586 static GSM_Statemachine *catcher=NULL;
2588 if (!catcher || catcher==state)
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)
2621 case GOP_Identify: {
2622 GSM_Error err,r=GE_NONE;
2623 if (GE_NONE!=(err=ATHW_GetImei (data,state)))
2625 if (GE_NONE!=(err=ATHW_GetRevision (data,state)))
2627 if (GE_NONE!=(err=ATHW_GetModel (data,state)))
2629 if (GE_NONE!=(err=ATHW_GetManufacturer(data,state)))
2635 return(GE_NOTIMPLEMENTED);
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 */
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 */