/* $Id$ G N O K I I A Linux/Unix toolset and driver for Nokia mobile phones. Copyright (C) 2001 Jan Kratochvil, based on code by Hugh Blemings & Pavel Janík ml. Released under the terms of the GNU GPL, see file COPYING for more details. This file provides an API for accessing SMS centers by CIMD and related protococols. See README-CIMD for more details on supported protocols. The various routines are prefixed by CIMD. $Log$ Revision 1.1.1.2 2002/04/03 01:44:15 short Implemented connection type "tcp" (GCT_TCP), use : as "port" Revision 1.1.1.1 2002/04/03 00:08:03 short Found in "gnokii-working" directory, some November-patches version */ #define CIMD_DEBUG 1 /* System header files */ #include #include #include #include #include #ifdef WIN32 #include #include "win32/winserial.h" #undef IN #undef OUT #define WRITEPHONE(a, b, c) WriteCommBlock(b, c) #define sleep(x) Sleep((x) * 1000) #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000)) extern HANDLE hPhone; #else #define WRITEPHONE(a, b, c) device_write(b, c) #include #include #include #include #include #include #include #include #include #include #include "device.h" #include "devices/unixserial.h" #endif /* Various header file */ #include "config.h" #include "misc.h" #include "gsm-common.h" #include "cfgreader.h" /* Global variables used by code in gsm-api.c to expose the functions supported by this model of phone. */ #if __unices__ /* fd opened in device.c */ extern int device_portfd; #endif /* Our private defines */ /* Define if plain (non-Bin) Submit is needed for 7bit messages */ #define CIMD_SUBMIT_7BIT 1 /* When now catchbuffer was provided and must have some space to decode * OK/ERROR/... result codes. */ #define CIMD_CATCHBUFFER_LENGTH 0x400 /* +1 of maximum position number of CIMD protocol parameter */ #define CIMD_PARAMSLOTS (0x10) /* We assume the 'right one' class is 1 (Mobile Equipment specific) */ #define DEFAULT_CLASS 1 /* CIMD_Param_Nak_Error codes: */ #define CIMD_NAK_NO_SMS (0x5001) #define CIMD_NAK_KEEPALIVE_REPLY (0x9998) #define CIMD_NAK_RESEND (0x9999) /* Local variables */ #ifndef WIN32 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH]; #endif static bool RequestTerminate; static u8 CIMD_CatchBuffer[CIMD_CATCHBUFFER_LENGTH]; static u8 *CIMD_CatchBufferPtr=CIMD_CatchBuffer; /* current destination writing ptr */ static GSM_Error *CIMD_CatchBufferErrorP; static void CIMD_CatchBufferStart(GSM_Error *errorcodep); #define CIMD_MARK_START (0x02) #define CIMD_MARK_STOP (0x03) #define CIMD_MARK_SEP (0x09) #define CIMD_MARK_SEPS "\x09" /* ELF=ELement Format */ enum CIMD_ELF { CIMD_ELF_HexByte, CIMD_ELF_HexWord, CIMD_ELF_Decimal, CIMD_ELF_StringZ, CIMD_ELF_HexBlock, CIMD_ELF_Empty, }; struct CIMD_Param { u16 code; enum CIMD_ELF elf; }; enum CIMD_Param_SAMPLE { /* we need some universal enum for typecasting all params */ CIMD_Param_SAMPLE_dummy, }; /* CIMD_Cmd_Ack: */ enum CIMD_Param_Ack { CIMD_Param_Ack_Cmd, CIMD_Param_Ack_Error, CIMD_Param_Ack_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Ack[]={ { /* CIMD_Param_Ack_Cmd */ 1,CIMD_ELF_HexByte }, { /* CIMD_Param_Ack_Error */ 2,CIMD_ELF_StringZ }, /* protocol version on Cmd_Login, otherwise 0x0000 */ }; /* CIMD_Cmd_Nak: */ enum CIMD_Param_Nak { CIMD_Param_Nak_Cmd, CIMD_Param_Nak_Error, CIMD_Param_Nak_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Nak[]={ { /* CIMD_Param_Nak_Cmd */ 1,CIMD_ELF_HexByte }, { /* CIMD_Param_Nak_Error */ 2,CIMD_ELF_HexWord }, }; /* CIMD_Cmd_Login: */ enum CIMD_Param_Login { CIMD_Param_Login_ID, CIMD_Param_Login_PWD, CIMD_Param_Login_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Login[]={ { /* CIMD_Param_Login_ID */ 1,CIMD_ELF_StringZ }, { /* CIMD_Param_Login_PWD */ 2,CIMD_ELF_StringZ }, }; /* CIMD_Cmd_Logout: */ enum CIMD_Param_Logout { CIMD_Param_Logout_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Logout[]={ }; /* CIMD_Cmd_Retrieve: */ enum CIMD_Param_Retrieve { CIMD_Param_Retrieve_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Retrieve[]={ }; /* CIMD_Cmd_RetrieveReply: */ enum CIMD_Param_RetrieveReply { CIMD_Param_RetrieveReply_Destination, CIMD_Param_RetrieveReply_SourceApplication, /* ??? */ CIMD_Param_RetrieveReply_Text, CIMD_Param_RetrieveReply_Timestamp, CIMD_Param_RetrieveReply_Is8bit, /* protocol version 1.14+, CIMD_Param_DCSEnable_Type_*DCS* reqd */ CIMD_Param_RetrieveReply_PID_DCS, /* protocol version 1.15+, CIMD_Param_DCSEnable_Type_*PID_DCS* reqd */ CIMD_Param_RetrieveReply_SPEC, /* protocol version 1.16+, CIMD_Param_DCSEnable_Type_*SPEC* reqd */ CIMD_Param_RetrieveReply_NULL }; static const struct CIMD_Param CIMD_Param_BIP_RetrieveReply[]={ { /* CIMD_Param_RetrieveReply_Destination */ 1,CIMD_ELF_StringZ }, { /* CIMD_Param_RetrieveReply_SourceApplication */ 2,CIMD_ELF_StringZ }, { /* CIMD_Param_RetrieveReply_Text */ 3,CIMD_ELF_StringZ }, { /* CIMD_Param_RetrieveReply_Timestamp */ 4,CIMD_ELF_StringZ }, { /* CIMD_Param_RetrieveReply_Is8bit */ 5,CIMD_ELF_Decimal }, { /* CIMD_Param_RetrieveReply_PID_DCS */ 6,CIMD_ELF_Decimal }, /* upper=PID, lower=DCS, FIXME: should be HexWord! */ { /* CIMD_Param_RetrieveReply_SPEC */ 7,CIMD_ELF_Decimal }, /* FIXME: should be HexByte! */ }; /* CIMD_Cmd_Count: */ enum CIMD_Param_Count { CIMD_Param_Count_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Count[]={ }; /* CIMD_Cmd_CountReply: */ enum CIMD_Param_CountReply { CIMD_Param_CountReply_Used, /* Used==NotRead==Slots */ CIMD_Param_CountReply_NULL }; static const struct CIMD_Param CIMD_Param_BIP_CountReply[]={ { /* CIMD_Param_CountReply_Used */ 1,CIMD_ELF_Decimal }, }; /* CIMD_Cmd_Submit: */ enum CIMD_Param_Submit { CIMD_Param_Submit_Destination, CIMD_Param_Submit_Text, CIMD_Param_Submit_ValidityPeriod, CIMD_Param_Submit_AUX, /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */ CIMD_Param_Submit_DCS, /* protocol version 1.14+ */ CIMD_Param_Submit_PID, /* protocol version 1.15+ */ CIMD_Param_Submit_SPEC, /* protocol version 1.16+ */ CIMD_Param_Submit_NULL }; static const struct CIMD_Param CIMD_Param_BIP_Submit[]={ { /* CIMD_Param_Submit_Destination */ 1,CIMD_ELF_StringZ }, { /* CIMD_Param_Submit_Text */ 2,CIMD_ELF_StringZ }, { /* CIMD_Param_Submit_ValidityPeriod */ 3,CIMD_ELF_HexByte }, { /* CIMD_Param_Submit_AUX */ 4,CIMD_ELF_Empty }, { /* CIMD_Param_Submit_DCS */ 5,CIMD_ELF_Decimal }, { /* CIMD_Param_Submit_PID */ 6,CIMD_ELF_HexByte }, { /* CIMD_Param_Submit_SPEC */ 7,CIMD_ELF_HexByte }, }; /* CIMD_Cmd_SubmitBin: protocol version 1.12+ */ enum CIMD_Param_SubmitBin { CIMD_Param_SubmitBin_Destination, CIMD_Param_SubmitBin_Text, CIMD_Param_SubmitBin_ValidityPeriod, CIMD_Param_SubmitBin_AUX, /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */ CIMD_Param_SubmitBin_DCS, /* protocol version 1.14+ */ CIMD_Param_SubmitBin_PID, /* protocol version 1.15+ */ CIMD_Param_SubmitBin_SPEC, /* protocol version 1.16+ */ CIMD_Param_SubmitBin_NULL }; static const struct CIMD_Param CIMD_Param_BIP_SubmitBin[]={ { /* CIMD_Param_SubmitBin_Destination */ 1,CIMD_ELF_StringZ }, { /* CIMD_Param_SubmitBin_Text */ 2,CIMD_ELF_StringZ }, { /* CIMD_Param_SubmitBin_ValidityPeriod */ 3,CIMD_ELF_HexByte }, { /* CIMD_Param_SubmitBin_AUX */ 4,CIMD_ELF_Empty }, { /* CIMD_Param_SubmitBin_DCS */ 5,CIMD_ELF_Decimal }, { /* CIMD_Param_SubmitBin_PID */ 6,CIMD_ELF_HexByte }, { /* CIMD_Param_SubmitBin_SPEC */ 7,CIMD_ELF_HexByte }, }; /* CIMD_Cmd_DCSEnable: protocol version 1.14+ */ enum CIMD_Param_DCSEnable { CIMD_Param_DCSEnable_Type, CIMD_Param_DCSEnable_NULL }; static const struct CIMD_Param CIMD_Param_BIP_DCSEnable[]={ { /* CIMD_Param_DCSEnable_Type */ 1,CIMD_ELF_HexWord }, }; enum CIMD_Param_DCSEnable_Type { CIMD_Param_DCSEnable_Type_None =0x0000, /* protocol version 1.14+ */ CIMD_Param_DCSEnable_Type_DCS =0x0001, /* protocol version 1.14+ */ CIMD_Param_DCSEnable_Type_PID_DCS =0x0002, /* protocol version 1.15+ */ CIMD_Param_DCSEnable_Type_PID_DCS_SPEC=0x0003, /* protocol version 1.16+ */ }; #define CIMD_PARAM_ENTRY(param) (param),ARRAY_LEN((param)) struct CIMD_Cmd { u8 code; const struct CIMD_Param *param; unsigned paramcnt; }; enum CIMD_Cmd_Type { CIMD_Cmd_Ack, CIMD_Cmd_Nak, CIMD_Cmd_Login, CIMD_Cmd_Logout, CIMD_Cmd_Submit, CIMD_Cmd_Retrieve, CIMD_Cmd_RetrieveReply, CIMD_Cmd_Count, CIMD_Cmd_CountReply, CIMD_Cmd_SubmitBin, /* protocol version 1.12+ */ CIMD_Cmd_DCSEnable, /* protocol version 1.14+ */ CIMD_Cmd_NULL /* stdarg termination, MUST be last! */ }; static const struct CIMD_Cmd CIMD_Cmd_BIP[]={ { /* CIMD_Cmd_Ack */ 0x00,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Ack ) }, { /* CIMD_Cmd_Nak */ 0x99,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Nak ) }, { /* CIMD_Cmd_Login */ 0x01,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Login ) }, { /* CIMD_Cmd_Logout */ 0x02,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Logout ) }, { /* CIMD_Cmd_Submit */ 0x03,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Submit ) }, { /* CIMD_Cmd_Retrieve */ 0x05,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Retrieve ) }, { /* CIMD_Cmd_RetrieveReply */ 0x06,CIMD_PARAM_ENTRY(CIMD_Param_BIP_RetrieveReply) }, { /* CIMD_Cmd_Count */ 0x51,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Count ) }, { /* CIMD_Cmd_CountReply */ 0x61,CIMD_PARAM_ENTRY(CIMD_Param_BIP_CountReply ) }, { /* CIMD_Cmd_SubmitBin */ 0x31,CIMD_PARAM_ENTRY(CIMD_Param_BIP_SubmitBin ) }, { /* CIMD_Cmd_DCSEnable */ 0x53,CIMD_PARAM_ENTRY(CIMD_Param_BIP_DCSEnable ) }, }; static const struct CIMD_Cmd *CIMD_Cmd=CIMD_Cmd_BIP; /* FIXME: When other protocols get supported... */ static unsigned CIMD_Cmdcnt=ARRAY_LEN(CIMD_Cmd_BIP); /* FIXME: When other protocols get supported... */ struct CIMD_Param_Ack_Nak_Error { u16 code; const char *msg; }; static const struct CIMD_Param_Ack_Nak_Error CIMD_Param_Ack_Nak_Error[]={ /* CIMD_Cmd_Login */ { 0x1101,N_("User is already logged in") }, { 0x1001,N_("Logging in is currently disabled") }, { 0x9999,N_("Username not found") }, /* 0x000? can be bitmask combined from: */ { 0x0001,N_("Invalid password") }, { 0x0002,N_("User has forbidden access") }, { 0x0008,N_("User is already registered") }, /* ??? difference from 0x1101 ? */ /* CIMD_Cmd_Retrieve / CIMD_Cmd_Count */ { 0x5001,N_("No SMS found or not detectable") }, /* ==CIMD_NAK_NO_SMS */ { 0x5011,N_("Function not enabled") }, /* CIMD_Cmd_Retrieve */ { 0x5021,N_("Automatical retrieve is active") }, /* CIMD_Cmd_Submit / CIMD_Cmd_SubmitBin */ { 0x3001,N_("Error during processing SMS in BMG") }, { 0x3061,N_("Destination number has invalid format") }, /* CIMD_Cmd_SubmitBin */ { 0x3051,N_("Invalid DCS") }, /* protocol version 1.14+ */ { 0x3081,N_("Invalid Text") }, /* protocol version 1.14+ */ { 0x3091,N_("SMS is empty") }, /* protocol version 1.14+ */ { 0x3099,N_("SMS submit too fast") }, /* protocol version 1.16+ */ }; static struct CIMD_paramslot CIMD_RX_Packet_slot[CIMD_PARAMSLOTS]; #define CIMD_RX_PACKET_PARAMSLOT(paramsample) (CIMD_RX_Packet_slot+1+(unsigned)(paramsample)) static enum CIMD_Cmd_Type CIMD_RX_Packet_Cmd; static unsigned CIMD_RX_Packet_slots; /* RETURNS: Processed */ typedef bool (*CIMD_RX_PatrolFunc)(void); typedef void (*CIMD_RX_PatrolReset)(void); struct CIMD_RX_Patrol { enum CIMD_Cmd_Type cmd; CIMD_RX_PatrolFunc func; CIMD_RX_PatrolReset reset; }; static const struct CIMD_RX_Patrol *CIMD_RX_Patrol_Current; static char *CIMD_RX_Patrol_Ack_Error; static u16 CIMD_RX_Patrol_Nak_Error; #ifndef WIN32 static pthread_t Thread; # if __unices__ static pthread_t selThread; # endif #endif /* Local variables used by get/set phonebook entry code. Buffer is used as a source or destination for phonebook data and other functions... Error is set to GE_NONE by calling function, set to GE_COMPLETE or an error code by handler routines as appropriate. */ static GSM_SMSMessage *CurrentSMSMessage; static GSM_Error CurrentSMSMessageError; static GSM_SMSStatus *CurrentSMSStatus; static GSM_Error CurrentSMSStatusError; static unsigned char Revision[GSM_MAX_REVISION_LENGTH]; static unsigned char Model [GSM_MAX_MODEL_LENGTH]; static u8 CIMD_TX_SendCommand_buf[sizeof(CIMD_CatchBuffer)]; /* sizeof() is not required to be == but it is appropriate */ static u8 *CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf; struct CIMD_paramslot { u16 code; enum CIMD_ELF elf; union { struct { u8 i; } HexByte; struct { u16 i; } HexWord; struct { int i; } Decimal; struct { char *s; } StringZ; struct { u8 *buf; size_t len; } HexBlock; struct { int _dummy; } Empty; } u; }; /* slots are sorted by moving the whole blocks but they are small so it is OK */ static struct CIMD_paramslot CIMD_TX_SendCommand_paramslots[CIMD_PARAMSLOTS]; static int CIMD_TX_SendCommand_paramslots_full; static enum CIMD_Cmd_Type CIMD_TX_SendCommand_Cmd; /* RETURNS: Success */ static bool CIMD_TX_SendCommand_vpushparam(u16 code,enum CIMD_ELF elf,va_list *app) { struct CIMD_paramslot *slot=CIMD_TX_SendCommand_paramslots+(CIMD_TX_SendCommand_paramslots_full++); if (slot>=CIMD_TX_SendCommand_paramslots+ARRAY_LEN(CIMD_TX_SendCommand_paramslots)) { /* assertion */ fprintf(stderr,"Out of param slots!\n"); return(false); } slot->code=code; slot->elf=elf; switch (elf) { case CIMD_ELF_HexByte: slot->u.HexByte.i=va_arg((*app),int); break; case CIMD_ELF_HexWord: slot->u.HexWord.i=va_arg((*app),int); break; case CIMD_ELF_Decimal: slot->u.Decimal.i=va_arg((*app),int); break; case CIMD_ELF_StringZ: slot->u.StringZ.s=va_arg((*app),char *); break; case CIMD_ELF_HexBlock: slot->u.HexBlock.buf=va_arg((*app),u8 *); slot->u.HexBlock.len=va_arg((*app),size_t); break; case CIMD_ELF_Empty: break; } return(true); } /* RETURNS: Success */ static bool CIMD_TX_SendCommand_pushparam(u16 code,enum CIMD_ELF elf,...) { va_list ap; bool r; va_start(ap,elf); r=CIMD_TX_SendCommand_vpushparam(code,elf,&ap); va_end(ap); return(r); } static int CIMD_TX_SendCommand_paramslots_compare (const struct CIMD_paramslot *a,const struct CIMD_paramslot *b) { return((b->codecode)-(a->codecode)); } /* RETURNS: Success */ static bool CIMD_TX_SendCommand_storeparam(struct CIMD_paramslot *slot) { switch (slot->elf) { case CIMD_ELF_HexByte: CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",slot->u.HexByte.i); break; case CIMD_ELF_HexWord: CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%04X",slot->u.HexWord.i); break; case CIMD_ELF_Decimal: CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%d",slot->u.Decimal.i); break; case CIMD_ELF_StringZ: { size_t len; if (!slot->u.StringZ.s) /* NULL is interpreted as "" */ break; len=strlen(slot->u.StringZ.s); if (CIMD_TX_SendCommand_buf_d+len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) return(false); /* error - overflow */ memcpy(CIMD_TX_SendCommand_buf_d,slot->u.StringZ.s,len); CIMD_TX_SendCommand_buf_d+=len; } break; case CIMD_ELF_HexBlock: if (CIMD_TX_SendCommand_buf_d+2*slot->u.HexBlock.len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) return(false); /* error - overflow */ CIMD_TX_SendCommand_buf_d=SMS_BlockToHex(CIMD_TX_SendCommand_buf_d, slot->u.HexBlock.buf,slot->u.HexBlock.len); /* never fails */ break; case CIMD_ELF_Empty: break; } return(true); /* success */ } static unsigned CIMD_KeepAlives=0; static pthread_mutex_t CIMD_KeepAlivesLock; /* This function is NOT thread-safe! */ static void CIMD_KeepAlivesLockInit(void) { static bool done=false; if (done) return; pthread_mutex_init(&CIMD_KeepAlivesLock,NULL); done=true; } static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...); static void CIMD_KeepAlivesCheck(void) { unsigned alives; CIMD_KeepAlivesLockInit(); pthread_mutex_lock(&CIMD_KeepAlivesLock); alives=CIMD_KeepAlives; CIMD_KeepAlives=0; pthread_mutex_unlock(&CIMD_KeepAlivesLock); while (alives) { CIMD_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/, CIMD_Cmd_Nak, CIMD_Param_Nak_Cmd,(CIMD_Cmd+(unsigned)CIMD_Cmd_Ack)->code, /* ==0x00 */ CIMD_Param_Nak_Error,CIMD_NAK_KEEPALIVE_REPLY, /* ==0x9998 */ CIMD_Param_Nak_NULL); alives--; } } /* RETURNS: Success */ static bool CIMD_TX_SendCommandResend(boid) { int writephone_got; writephone_got=WRITEPHONE(PortFD, CIMD_TX_SendCommand_buf, CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf); #ifdef CIMD_DEBUG write(1,"CMD:",9); write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1)); write(1,"\n",6); #endif return(writephone_got==(CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf)); } static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...) { va_list ap; const struct CIMD_Cmd *cmdstruct; enum CIMD_Param_SAMPLE param; const struct CIMD_Param *paramstruct; u8 *u8s,xsum; struct CIMD_paramslot *slot; int parami; CIMD_KeepAlivesCheck(); va_start(ap,cmd); if (errorcodep) *errorcodep=GE_BUSY; CIMD_RX_Patrol_Current=patrol; CIMD_TX_SendCommand_Cmd=cmd; CIMD_TX_SendCommand_paramslots_full=0; if ((unsigned)cmd>=CIMD_Cmdcnt) goto fail; /* assert */ cmdstruct=CIMD_Cmd+(unsigned)cmd; if (!CIMD_TX_SendCommand_pushparam(0/*code*/,CIMD_ELF_HexByte/*elf*/,cmdstruct->code)) goto fail; for (;;) { param=va_arg(ap,enum CIMD_Param_SAMPLE); if ((unsigned)param==cmdstruct->paramcnt) break; /* CIMD_Param_*_NULL reached */ if ((unsigned)param>cmdstruct->paramcnt) goto fail; /* assert */ paramstruct=(cmdstruct->param+(unsigned)param); if (!CIMD_TX_SendCommand_vpushparam(paramstruct->code,paramstruct->elf,&ap)) goto fail; } qsort(CIMD_TX_SendCommand_paramslots,CIMD_TX_SendCommand_paramslots_full, sizeof(*CIMD_TX_SendCommand_paramslots), (int (*)(const void *,const void *))CIMD_TX_SendCommand_paramslots_compare); CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf; *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_START; slot=CIMD_TX_SendCommand_paramslots; for (parami=0;slot CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) { /* error - overflow */ fail: if (errorcodep) *errorcodep=GE_INTERNALERROR; return; } if (slot->code==parami) { if (!CIMD_TX_SendCommand_storeparam(slot)) /* error */ goto fail; } if (slot->code>=parami) *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_SEP; if (slot->code<=parami) slot++; } xsum=0; for (u8s=CIMD_TX_SendCommand_buf;u8sSMSC link */ static unsigned char CIMD_SMStoSPEC_BIP_CIMD(GSM_SMSMessage *SMS) { return(0 /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii */ |((!!SMS->ReplyViaSameSMSC)<<1) /*ReplyPath*/ |((!!SMS->UDHPresent)<<2) /*UDH set*/ ); } static unsigned char CIMD_SMStoDCS(GSM_SMSMessage *SMS) { int class=(SMS->Class==-1 ? DEFAULT_CLASS : SMS->Class); #if 0 /* NEVER send 0 for BIP CIMD as it would translate it to F6 !!! */ if (!SMS->EightBit && class==DEFAULT_CLASS) return(0x00); #endif return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0)); } static GSM_Error CIMD_PhoneSetup(void) { #if 1 /* HACK */ CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,100/*timeout*/,NULL/*patrol*/, CIMD_Cmd_Login, /* NULLs will be interpreted as "" */ CIMD_Param_Login_ID,CFG_Get(CFG_Info,"CIMD","name"), CIMD_Param_Login_PWD,CFG_Get(CFG_Info,"CIMD","password"), CIMD_Param_Login_NULL); if (!CIMD_RX_Patrol_Ack_Error) Revision[0]='\0'; else SAFE_STRNCPY_SIZEOF(Revision,CIMD_RX_Patrol_Ack_Error); CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/, CIMD_Cmd_DCSEnable, CIMD_Param_DCSEnable_Type,(u16)CIMD_Param_DCSEnable_Type_PID_DCS_SPEC, CIMD_Param_DCSEnable_NULL); #endif return(GE_NONE); } static void CIMD_RX_Char(char rx_byte); static void CIMD_SigHandler(int status); static bool CIMD_OpenSerial(GSM_ConnectionType connection); GSM_Phone phone_cimd; /* forward declaration */ /* Initialise variables and state machine. */ static GSM_Error CIMD_Init(GSM_Data *data, GSM_Statemachine *state) { RequestTerminate = false; SAFE_STRNCPY_SIZEOF(Model,data->Model); /* Create and start main thread. */ #ifdef WIN32 { int rtn; rtn = ! OpenConnection(State->Link.PortDevice,CIMD_RX_Char,CIMD_KeepAliveProc); if (rtn != 0) { return(GE_INTERNALERROR); } #else SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice); if (!CIMD_OpenSerial(state->Link.ConnectionType)) return(GE_INTERNALERROR); #endif CIMD_ERR_WRAPPER(CIMD_PhoneSetup()); return (GE_NONE); } #if __unices__ /* thread for handling incoming data */ void CIMD_SelectLoop() { int err; fd_set readfds; struct timeval timeout; FD_ZERO(&readfds); FD_SET(device_portfd, &readfds); /* set timeout to 15 seconds */ timeout.tv_sec=15; timeout.tv_usec=0; while (!RequestTerminate) { err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout); /* call singal handler to process incoming data */ if ( err > 0 ) CIMD_SigHandler(0); else if (err == -1) perror("Error in SelectLoop"); } } #endif /* Applications should call CIMD_Terminate to shut down the CIMD thread and close the serial port. */ static GSM_Error CIMD_Terminate(GSM_Data *data, GSM_Statemachine *state) { GSM_Error err; #if 1 /* HACK */ /* Don't wait too much as we can have already broken link */ CIMD_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/, CIMD_Cmd_Logout, CIMD_Param_Logout_NULL); wait_on(&err,20/*timeout*/); /* errors ignored, of course */ #endif /* Request termination of thread */ RequestTerminate = true; #ifndef WIN32 /* Now wait for thread to terminate. */ pthread_join(Thread, NULL); /* Close serial port. */ device_close(); #else CloseConnection(); #endif return(GE_NONE); } /* messagecenter->No" is NOT set as it is input argument */ static void CIMD_MessageCenterClear(GSM_MessageCenter *messagecenter) { messagecenter->Name[0]='\0'; /* not present up to Nokia 9110i */ messagecenter->Recipient[0]='\0'; /* not present up to Nokia 9110i */ messagecenter->Number[0]='\0'; /* default */ messagecenter->Format=GSMF_Text; /* default */ messagecenter->Validity=GSMV_72_Hours; /* default */ } static bool CIMD_RX_Patrol_CountReply(void) { CurrentSMSStatus->UnRead=/*FALLTHRU*/ CurrentSMSStatus->Used =/*FALLTHRU*/ CurrentSMSStatus->Slots =/*FALLTHRU*/ CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_CountReply_Used)->u.Decimal.i; CurrentSMSStatusError=GE_NONE; return(true); } static const struct CIMD_RX_Patrol CIMD_RX_Patrol_CountReply_struct= { CIMD_Cmd_CountReply,CIMD_RX_Patrol_CountReply }; static GSM_Error CIMD_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state) { CurrentSMSStatus = data->SMSStatus; CurrentSMSStatus->UnRead=0; /* default */ CurrentSMSStatus->Used =0; /* default */ CurrentSMSStatus->Slots =0; /* default */ #if 1 /* HACK */ CIMD_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,100/*timeout*/,&CIMD_RX_Patrol_CountReply_struct/*patrol*/, CIMD_Cmd_Count, CIMD_Param_Count_NULL); #endif return(GE_NONE); } static GSM_Error CIMD_GetImei(GSM_Data *data, GSM_Statemachine *state) { /* not supported by the protocol */ data->Imei[0]='\0'; return (GE_NONE); } static GSM_Error CIMD_GetRevision(GSM_Data *data, GSM_Statemachine *state) { if (*Revision) { strcpy(data->Revision,Revision); return (GE_NONE); } else return (GE_TRYAGAIN); } static GSM_Error CIMD_GetModel(GSM_Data *data, GSM_Statemachine *state) { /* not supported by the protocol */ strcpy(data->Model,Model); return (GE_NONE); } static GSM_Error CIMD_GetManufacturer(GSM_Data *data, GSM_Statemachine *state) { /* not supported by the protocol */ data->Imei[0]='\0'; return (GE_NONE); } static void CIMD_DateTimeSetCurrent(GSM_DateTime *datetime) { time_t current=time(NULL); struct tm *tm=localtime(¤t); datetime->AlarmEnabled=false; datetime->Year =tm->tm_year+1900; datetime->Month =tm->tm_mon+1; datetime->Day =tm->tm_mday; datetime->Hour =tm->tm_hour; datetime->Minute=tm->tm_min; datetime->Second=tm->tm_sec; datetime->Timezone=timezone; } /* scts=="18.06.1998 22:33:23" */ static bool CIMD_SCTStoSMS(GSM_SMSMessage *SMS,const char *scts) { GSM_DateTime *DateTime=&SMS->Time; const char *fmt="%2d.%2d.%4d %2d:%2d:%2d"; /* trailing '\0' IS used! */ const char *fs,*ss; fs=fmt; ss=scts; do { while (*fs=='%') { int nums=(*++fs)-'0'; while (nums--) if (!isdigit(*ss++)) return(false); } if (*fs++!=*ss) return(false); } while (fs[-1]); /* string is completely valid now */ sscanf(scts,fmt, &DateTime->Day, &DateTime->Month, &DateTime->Year, &DateTime->Hour, &DateTime->Minute, &DateTime->Second); DateTime->Timezone=0; return(true); } static void CIMD_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs) { switch ((dcs&0xF0)>>4) { case 0x0: switch (dcs&0x0F) { case 0x0: CurrentSMSMessage->EightBit=false; break; } break; case 0xF: CurrentSMSMessage->EightBit=!!(dcs&0x04); /* bit 2 */ CurrentSMSMessage->Class=(dcs&0x03); /* bits 0 & 1 */ break; } } /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link */ static void CIMD_SPEC_BIP_CIMDtoSMS(GSM_SMSMessage *SMS,u8 spec) { /* Such specification not supported by BIP: */ CurrentSMSMessage->Type=GST_MT; CurrentSMSMessage->Status=GSS_NOTSENTREAD; /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii */ SMS->UDHPresent=!!(spec&(1<<2)); SMS->ReplyViaSameSMSC=!!(spec&(1<<1)); } static bool CIMD_RX_Patrol_RetrieveReply(void) { u16 pid_dcs; /* upper=PID, lower=DCS */ { const char *destination=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Destination)->u.StringZ.s; if (strlen(destination)+1 > sizeof(CurrentSMSMessage->Sender)) { fail: CurrentSMSMessageError=GE_INTERNALERROR; return(true); /* error */ } strcpy(CurrentSMSMessage->Sender,destination); } if ((CurrentSMSMessage->EightBit=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Is8bit)->u.Decimal.i)) { const char *hextext=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s; size_t hextextlen=strlen(hextext); if ((hextextlen&1) || (hextextlen/2 > sizeof(CurrentSMSMessage->MessageText))) goto fail; /* error - message too long */ if (!SMS_BlockFromHex(CurrentSMSMessage->MessageText/*d*/,hextext,hextextlen)) goto fail; /* error - parse error */ CurrentSMSMessage->MessageTextLength=hextextlen/2; } else { /* 7bit */ const char *text=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s; size_t textlen=strlen(text); if (textlen+1 > sizeof(CurrentSMSMessage->MessageText)) goto fail; /* error - message too long */ strcpy(CurrentSMSMessage->MessageText,text); CurrentSMSMessage->MessageTextLength=textlen; } /* errors ignored as it is not fatal */ CIMD_SCTStoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Timestamp)->u.StringZ.s); pid_dcs=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_PID_DCS)->u.HexWord.i; /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */ CurrentSMSMessage->MessageCenter.Format=(GSM_SMSMessageFormat)(pid_dcs>>8U); /* */ CIMD_DCStoSMS(CurrentSMSMessage,(pid_dcs&0x00FFU)); CIMD_SPEC_BIP_CIMDtoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_SPEC)->u.HexByte.i); if (CurrentSMSMessage->UDHPresent) { u8 udhlen; if (CurrentSMSMessage->MessageTextLength<=0) goto fail; udhlen=1/*sizeof(udhlen)*/ +CurrentSMSMessage->MessageText[0]; if (udhlen > CurrentSMSMessage->MessageTextLength) goto fail; memcpy(CurrentSMSMessage->UDH,CurrentSMSMessage->MessageText,udhlen); memmove(CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageText+udhlen, CurrentSMSMessage->MessageTextLength-udhlen +1/*Trailing '\0'*/); CurrentSMSMessage->MessageTextLength-=udhlen; } CurrentSMSMessageError=GE_NONE; return(true); } static const struct CIMD_RX_Patrol CIMD_RX_Patrol_RetrieveReply_struct= { CIMD_Cmd_RetrieveReply,CIMD_RX_Patrol_RetrieveReply }; static GSM_Error CIMD_GetSMS(GSM_Data *data, GSM_Statemachine *state) { CurrentSMSMessage = data->SMSMessage; if (CurrentSMSMessage->MemoryType!=GMT_SM) return(GE_INVALIDMEMORYTYPE); CIMD_DateTimeSetCurrent(&CurrentSMSMessage->Time); /* default */ CIMD_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime); /* not present in the protocol */ CurrentSMSMessage->MessageTextLength=0; /* default */ CurrentSMSMessage->Validity=72/*hours*/*60; /* default */ CurrentSMSMessage->UDHPresent=false; /* default */ CurrentSMSMessage->MessageText[0]='\0'; /* default */ CIMD_MessageCenterClear(&CurrentSMSMessage->MessageCenter); /* default */ CurrentSMSMessage->MessageCenter.No=0; /* default - input for GetSMSCenter */ CurrentSMSMessage->Sender[0]='\0'; /* default */ CurrentSMSMessage->Destination[0]='\0'; /* default */ CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location; /* default */ /* CurrentSMSMessage->MemoryType is input argument */ CurrentSMSMessage->Type=GST_UN; /* default, detection of EMPTY SMSes! */ CurrentSMSMessage->Status=GSS_SENTREAD; /* default */ CurrentSMSMessage->Class=DEFAULT_CLASS; /* default */ CurrentSMSMessage->EightBit=false; /* default */ CurrentSMSMessage->Compression=false; /* default */ /* CurrentSMSMessage->Location is input argument */ CurrentSMSMessage->ReplyViaSameSMSC=false; /* default */ CIMD_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&CIMD_RX_Patrol_RetrieveReply_struct/*patrol*/, CIMD_Cmd_Retrieve, CIMD_Param_Retrieve_NULL); if (GE_NONE!=wait_on(&CurrentSMSMessageError,90/*timeout*/)) { if (CIMD_RX_Patrol_Nak_Error!=CIMD_NAK_NO_SMS) return(CurrentSMSMessageError); /* We don't return GE_EMPTYSMSLOCATION as when we have already eaten * all the wating SMSes there cannot be any other one */ return(GE_INVALIDSMSLOCATION); } return(GE_NONE); } static GSM_Error CIMD_SendSMS(GSM_Data *data, GSM_Statemachine *state) { u8 bintext[sizeof(CurrentSMSMessage->UDH)+sizeof(CurrentSMSMessage->MessageText)],*d; char hexbintext[sizeof(bintext) +1/*Trailing '\0'*/]; CurrentSMSMessage = data->SMSMessage; CurrentSMSMessage->MessageNumber=0; /* default */ d=bintext; if (CurrentSMSMessage->UDHPresent) { size_t UDHlen=1+CurrentSMSMessage->UDH[0]; memcpy(d,CurrentSMSMessage->UDH,UDHlen); d+=UDHlen; #ifdef CIMD_SUBMIT_7BIT if (!CurrentSMSMessage->EightBit) /* We are not able to send UDH by 7bit channel */ return(GE_NOTSUPPORTED); #endif } if ( #ifdef CIMD_SUBMIT_7BIT 1 /* Never do 7bit->8bit encoding when CIMD_SUBMIT_7BIT */ #else CurrentSMSMessage->EightBit #endif ) { memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength); d+=CurrentSMSMessage->MessageTextLength; } #ifndef CIMD_SUBMIT_7BIT else { size_t byteslen=PackSevenBitsToEight( /* check it out yourself, really the number of used bits for UDH header on the start * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary */ (7-(d-bintext))%7, CurrentSMSMessage->MessageText,d); d+=byteslen; } #endif if ( #ifdef CIMD_SUBMIT_7BIT CurrentSMSMessage->EightBit #else 1 /* Never use plain Submit for 8bit messages */ #endif ) { *(SMS_BlockToHex(hexbintext,bintext,(d-bintext)/*len*/))='\0'; /* DANGER: Keep in sync with CIMD_Cmd_Submit !!! */ CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/, CIMD_Cmd_SubmitBin, CIMD_Param_SubmitBin_Destination,CurrentSMSMessage->Destination, CIMD_Param_SubmitBin_Text,hexbintext, CIMD_Param_SubmitBin_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity), CIMD_Param_SubmitBin_AUX, /* Empty */ CIMD_Param_SubmitBin_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage), CIMD_Param_SubmitBin_PID,(u8)CurrentSMSMessage->MessageCenter.Format, CIMD_Param_SubmitBin_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage), CIMD_Param_SubmitBin_NULL); } #ifdef CIMD_SUBMIT_7BIT else { *d='\0'; /* DANGER: Keep in sync with CIMD_Cmd_SubmitBin !!! */ CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/, CIMD_Cmd_Submit, CIMD_Param_Submit_Destination,CurrentSMSMessage->Destination, CIMD_Param_Submit_Text,bintext, CIMD_Param_Submit_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity), CIMD_Param_Submit_AUX, /* Empty */ CIMD_Param_Submit_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage), CIMD_Param_Submit_PID,(u8)CurrentSMSMessage->MessageCenter.Format, CIMD_Param_Submit_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage), CIMD_Param_Submit_NULL); } #endif return(GE_SMSSENDOK); } static GSM_Error CIMD_Reset(GSM_Data *data, GSM_Statemachine *state) { CIMD_ERR_WRAPPER(CIMD_PhoneSetup()); return(GE_NONE); } #ifndef WIN32 /* Called by initialisation code to open comm port in asynchronous mode. */ static bool CIMD_OpenSerial(GSM_ConnectionType connection) { int result; #if __unices__ int rtn; #else struct sigaction sig_io; /* Set up and install handler before enabling async IO on port. */ sig_io.sa_handler = CIMD_SigHandler; sig_io.sa_flags = 0; sigaction (SIGIO, &sig_io, NULL); #endif /* Open device. */ result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, connection); if (!result) { perror(_("Couldn't open CIMD device")); return false; } #if __unices__ /* create a thread to handle incoming data from mobile phone */ rtn = pthread_create(&selThread, NULL, (void*)CIMD_SelectLoop, (void*)NULL); if (rtn != 0) return false; #endif /* device_changespeed() not needed as device_open() now automatically * sets the user-specified (or default) speed. */ return (true); } static void CIMD_SigHandler(int status) { unsigned char buffer[255]; int count, res; res = device_read(buffer, 255); for (count = 0; count < res ; count ++) CIMD_RX_Char(buffer[count]); } #endif /* WIN32 */ static bool CIMD_RX_Patrol_Ack(void) { if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i) { /* ==0x99 */ /* We cannot send the keepalive here as some dangerous operation may be in progress */ CIMD_KeepAlivesLockInit(); pthread_mutex_lock(&CIMD_KeepAlivesLock); CIMD_KeepAlives++; pthread_mutex_unlock(&CIMD_KeepAlivesLock); return(false); } if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code) return(false); /* Ack for some unknown command */ free(CIMD_RX_Patrol_Ack_Error); CIMD_RX_Patrol_Ack_Error=strdup(CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Error)->u.StringZ.s); /* Some patrol may have already indicated some error! */ if (CIMD_CatchBufferErrorP && *CIMD_CatchBufferErrorP==GE_BUSY) *CIMD_CatchBufferErrorP=GE_NONE; return(true); } static void CIMD_RX_Patrol_Ack_reset(void) { free(CIMD_RX_Patrol_Ack_Error); CIMD_RX_Patrol_Ack_Error=NULL; } static const char *CIMD_RX_Patrol_Nak_resolve(u16 errorcode) { const struct CIMD_Param_Ack_Nak_Error *errorp; for (errorp=CIMD_Param_Ack_Nak_Error;errorpcode) return(_(errorp->msg)); return(_("Unknown error code")); } static void CIMD_RX_Patrol_Nak_dump(u16 errorcode) { fprintf(stderr,_("Got CIMD error code 0x%04X: "),errorcode); if (!(errorcode & ~0x000F)) { /* combined error code */ u16 errormask; fprintf(stderr,_("combined:")); for (errormask=0x0001;errormask<=errorcode;errormask<<=1) if (errorcode&errormask) fprintf(stderr," +%s",CIMD_RX_Patrol_Nak_resolve(errormask)); fputc('\n',stderr); return; } fprintf(stderr,"%s\n",CIMD_RX_Patrol_Nak_resolve(errorcode)); } static bool CIMD_RX_Patrol_Nak(void) { if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i /* ==0x99 */ && CIMD_NAK_RESEND == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i) { /* 0x9999 */ fprintf(stderr,_("WARNING: Requested to resend last packet!!\n")); CIMD_TX_SendCommandResend(); return(true); } if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code) return(false); /* Nak for some unknown command */ CIMD_RX_Patrol_Nak_Error=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i; if (CIMD_CatchBufferErrorP) *CIMD_CatchBufferErrorP=GE_INTERNALERROR; CIMD_RX_Patrol_Nak_dump(CIMD_RX_Patrol_Nak_Error); return(true); } static void CIMD_RX_Patrol_Nak_reset(void) { CIMD_RX_Patrol_Nak_Error=0; } static const struct CIMD_RX_Patrol CIMD_RX_Patrols[]={ { CIMD_Cmd_Ack,CIMD_RX_Patrol_Ack,CIMD_RX_Patrol_Ack_reset }, { CIMD_Cmd_Nak,CIMD_RX_Patrol_Nak,CIMD_RX_Patrol_Nak_reset }, }; static void CIMD_CatchBufferReset(void) { CIMD_CatchBufferPtr=CIMD_CatchBuffer; } static void CIMD_CatchBufferStart(GSM_Error *errorcodep) { static GSM_Error err_trashcan; const struct CIMD_RX_Patrol *patrol; if (!errorcodep) errorcodep=&err_trashcan; CIMD_CatchBufferErrorP=errorcodep; CIMD_CatchBufferReset(); for (patrol=CIMD_RX_Patrols;patrolreset) (*patrol->reset)(); } static u8 *CIMD_RX_ProcessRawPacket_parseparam_buf_s; /* RETURNS: Success */ static bool CIMD_RX_ProcessRawPacket_parseparam(struct CIMD_paramslot *slot) { switch (slot->elf) { case CIMD_ELF_HexByte: { unsigned parsedbyte; if (0 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0]) || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1]) || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[2] ) return(false); if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedbyte)) return(false); slot->u.HexByte.i=parsedbyte; CIMD_RX_ProcessRawPacket_parseparam_buf_s+=2+1; } break; case CIMD_ELF_HexWord: { unsigned parsedword; if (0 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0]) || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1]) || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[2]) || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[3]) || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[4] ) return(false); if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedword)) return(false); slot->u.HexWord.i=parsedword; CIMD_RX_ProcessRawPacket_parseparam_buf_s+=4+1; } break; case CIMD_ELF_Decimal: { int parsedint; u8 *start=CIMD_RX_ProcessRawPacket_parseparam_buf_s; if (CIMD_MARK_SEP==*start) return(false); /* empty Decimal not permitted */ while (isdigit(*CIMD_RX_ProcessRawPacket_parseparam_buf_s)) CIMD_RX_ProcessRawPacket_parseparam_buf_s++; if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++) /* skip CIMD_MARK_SEP */ return(false); if (1!=sscanf(start,"%d" CIMD_MARK_SEPS,&parsedint)) return(false); slot->u.Decimal.i=parsedint; } break; case CIMD_ELF_StringZ: slot->u.StringZ.s=CIMD_RX_ProcessRawPacket_parseparam_buf_s; while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s) CIMD_RX_ProcessRawPacket_parseparam_buf_s++; *CIMD_RX_ProcessRawPacket_parseparam_buf_s++='\0'; /* skip CIMD_MARK_SEP */ break; case CIMD_ELF_HexBlock: slot->u.HexBlock.buf=CIMD_RX_ProcessRawPacket_parseparam_buf_s; while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s) CIMD_RX_ProcessRawPacket_parseparam_buf_s++; if ((CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf)&1) return(false); /* odd number of xdigits */ slot->u.HexBlock.len=(CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf); if (!SMS_BlockFromHex(slot->u.HexBlock.buf/*d*/,slot->u.HexBlock.buf/*s*/,slot->u.HexBlock.len*2)) return(false); /* parse error */ CIMD_RX_ProcessRawPacket_parseparam_buf_s++; /* skip CIMD_MARK_SEP */ break; case CIMD_ELF_Empty: if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s) return(false); /* some content found */ break; } return(true); /* success */ } static bool CIMD_RX_EvalPatrol(const struct CIMD_RX_Patrol *patrol) { if (!patrol) return(false); /* not recognized */ if (CIMD_RX_Packet_Cmd!=patrol->cmd) return(false); /* not recognized */ return((*patrol->func)()); } static inline void CIMD_RX_ProcessPacket(void) { const struct CIMD_RX_Patrol *patrol; for (patrol=CIMD_RX_Patrols;patrol",9); write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1)); write(1,"\n",6); #endif if (CIMD_CatchBufferPtrcode==CIMD_RX_Packet_slot[0].u.HexByte.i) break; if (cmdp>=CIMD_Cmd+CIMD_Cmdcnt) return; /* error - unknown command */ CIMD_RX_Packet_Cmd=(enum CIMD_Cmd_Type)(cmdp-CIMD_Cmd); /* "position" handling is BIP dependency! */ for (position=1;CIMD_RX_ProcessRawPacket_parseparam_buf_sparam;paramparam+cmdp->paramcnt;param++) if (position==param->code) break; if (param>=cmdp->param+cmdp->paramcnt) { /* not found - ignore */ while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++); /* skip CIMD_MARK_SEP */ continue; } slot=CIMD_RX_Packet_slot+1+(param-cmdp->param); slot->code=param->code; slot->elf=param->elf; if (!CIMD_RX_ProcessRawPacket_parseparam(slot)) return; /* error - unable to parse required param */ /* CIMD_MARK_SEP is skipped by CIMD_RX_ProcessRawPacket_parseparam() automatically */ CIMD_RX_Packet_slots++; } /* The packet is now parsed */ CIMD_RX_ProcessPacket(); } /* RX_State machine for receive handling. Called once for each character received from the phone/phone. */ static void CIMD_RX_Char(char rx_byte) { #ifdef CIMD_DEBUG #if 0 dprintf(_("Received character '%c' (0x%02X)\n"), (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte); #endif putchar(rx_byte); #endif /* NEVER store '\0' as it would knock-out completely our patrolling system! * It is invalid anyway */ if (rx_byte=='\0') { CIMD_CatchBufferReset(); return; } if (CIMD_CatchBufferPtr==CIMD_CatchBuffer && rx_byte!=CIMD_MARK_START) { /* No haven't yet catched any START yet */ return; } if (CIMD_CatchBufferPtr>=CIMD_CatchBuffer+sizeof(CIMD_CatchBuffer)) { fprintf(stderr,_("WARNING: Incoming CIMD packet too long to fit (>%d bytes)!\n"),sizeof(CIMD_CatchBuffer)); CIMD_CatchBufferReset(); } *CIMD_CatchBufferPtr++=rx_byte; if (rx_byte==CIMD_MARK_STOP) { CIMD_RX_ProcessRawPacket(); CIMD_CatchBufferReset(); } } /* Here we initialise model specific functions. */ #define CIMD_FUNCTIONS_ENTRY(name) \ case GOP_##name: return(CIMD_##name(data,state)); static GSM_Error CIMD_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state) { static GSM_Statemachine *catcher=NULL; if (!catcher || catcher==state) catcher=state; else *((char *)NULL)=1; switch (op) { CIMD_FUNCTIONS_ENTRY(Init) CIMD_FUNCTIONS_ENTRY(Terminate) CIMD_FUNCTIONS_ENTRY(GetSMSStatus) CIMD_FUNCTIONS_ENTRY(GetSMS) CIMD_FUNCTIONS_ENTRY(SendSMS) CIMD_FUNCTIONS_ENTRY(GetImei) CIMD_FUNCTIONS_ENTRY(GetRevision) CIMD_FUNCTIONS_ENTRY(GetModel) CIMD_FUNCTIONS_ENTRY(GetManufacturer) CIMD_FUNCTIONS_ENTRY(Reset) case GOP_Identify: { GSM_Error err,r=GE_NONE; if (GE_NONE!=(err=CIMD_GetImei (data,state))) r=err; if (GE_NONE!=(err=CIMD_GetRevision (data,state))) r=err; if (GE_NONE!=(err=CIMD_GetModel (data,state))) r=err; if (GE_NONE!=(err=CIMD_GetManufacturer(data,state))) r=err; return(r); } default: return(GE_NOTIMPLEMENTED); } } GSM_Phone phone_cimd = { UNIMPLEMENTED, /* IncomingFunctions - we don't use default StateMachine */ UNIMPLEMENTED, /* DefaultFunction - we don't use default StateMachine */ /* Mobile phone information */ { "BIP" /* |CIMD - not yet */, /* Supported models */ 100, /* Max RF Level (AT+CSQ) */ 0, /* Min RF Level (AT+CSQ) */ GRF_Percentage, /* RF level units */ 100, /* Max Battery Level (AT+CBC) */ 0, /* Min Battery Level (AT+CBC) */ GBU_Percentage, /* Battery level units */ GDT_None, /* Have date/time support */ GDT_None, /* Alarm supports time only */ 0, /* No alarm available */ 48, 84, /* Startup logo size */ 14, 72, /* Op logo size */ 14, 72, /* Caller logo size */ }, CIMD_Functions, };