--- /dev/null
+/*
+
+ $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.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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <limits.h>
+
+
+#ifdef WIN32
+
+#include <windows.h>
+#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 <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <errno.h>
+#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->code<a->code)-(a->code<b->code));
+}
+
+/* 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:<STX>",9);
+ write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1));
+ write(1,"<ETX>\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_paramslots+CIMD_TX_SendCommand_paramslots_full;parami++) {
+ if (CIMD_TX_SendCommand_buf_d +32/*safety*/ > 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;u8s<CIMD_TX_SendCommand_buf_d;u8s++)
+ xsum+=*u8s;
+ CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",(unsigned)xsum);
+ *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_STOP;
+
+ CIMD_CatchBufferStart(errorcodep);
+
+ if (!CIMD_TX_SendCommandResend())
+ goto fail;
+
+ /* success but we don't wait for the result code */
+}
+
+/* This function is used to get storage status from the phone. It currently
+ supports two different memory areas - internal and SIM. */
+
+static GSM_Error
+wait_on(volatile GSM_Error *what, int timeout)
+{
+GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
+
+ while (timeout && ((r=*what)==GE_BUSY)) {
+ if (!--timeout) {
+ r=GE_TIMEOUT;
+ break;
+ }
+ CIMD_KeepAlivesCheck();
+ usleep(100000);
+ }
+ /* any specific patrollers are no longer valid */
+ CIMD_RX_Patrol_Current=NULL;
+
+#ifdef CIMD_DEBUG
+ printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
+#endif
+
+ return(r);
+}
+
+#define WAIT_ON(what, timeout) \
+ do { \
+ GSM_Error res = wait_on(what, timeout); \
+ if (res != GE_NONE) \
+ return res; \
+ } while (0)
+
+/* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
+ */
+#define CIMD_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
+ do { \
+GSM_Error _CIMD_TX_SENDCOMMAND_WAIT_ON_err,*_CIMD_TX_SENDCOMMAND_WAIT_ON_errp; \
+ \
+ if (!(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
+ _CIMD_TX_SENDCOMMAND_WAIT_ON_errp=&_CIMD_TX_SENDCOMMAND_WAIT_ON_err; \
+ CIMD_TX_SendCommand(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
+ WAIT_ON(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
+ } while (0)
+
+
+#define CIMD_ERR_WRAPPER(expr) \
+ do { \
+ GSM_Error err=(expr); \
+ if (err!=GE_NONE) \
+ return(err); \
+ } while (0)
+
+/* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC 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(void);
+
+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())
+ 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); /* <pid> */
+ 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(void)
+{
+ 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*/, GCT_Serial);
+
+ 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;errorp<CIMD_Param_Ack_Nak_Error+ARRAY_LEN(CIMD_Param_Ack_Nak_Error);errorp++)
+ if (errorcode==errorp->code)
+ 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;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
+ if (patrol->reset)
+ (*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<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
+ if (CIMD_RX_EvalPatrol(patrol))
+ return; /* recognized and processed - whether successfuly is not interesting */
+ CIMD_RX_EvalPatrol(CIMD_RX_Patrol_Current);
+}
+
+static inline void CIMD_RX_ProcessRawPacket(void)
+{
+unsigned xsumgot;
+u8 *u8s,xsum;
+u16 position;
+const struct CIMD_Param *param;
+const struct CIMD_Cmd *cmdp;
+
+#ifdef CIMD_DEBUG
+ write(1,"GOT:<STX>",9);
+ write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1));
+ write(1,"<ETX>\n",6);
+#endif
+
+ if (CIMD_CatchBufferPtr<CIMD_CatchBuffer +1/*START*/ +1/*SEP*/ +2/*xsum*/ +1/*STOP*/)
+ return; /* error - buffer too short */
+ if (0 || CIMD_CatchBufferPtr[-4]!=CIMD_MARK_SEP
+ || !isxdigit(CIMD_CatchBufferPtr[-3])
+ || !isxdigit(CIMD_CatchBufferPtr[-2])
+ )
+ return; /* error - invalid buffer tail */
+
+ /* (*Ptr) will now point to the end of parameter part */
+ CIMD_CatchBufferPtr-=3;
+ if (1!=sscanf(CIMD_CatchBufferPtr,"%X" CIMD_MARK_SEPS,&xsumgot))
+ return; /* INTERNAL - we have already checked the validity! */
+ xsum=0;
+ for (u8s=CIMD_CatchBuffer;u8s<CIMD_CatchBufferPtr;u8s++)
+ xsum+=*u8s;
+ if (xsum!=xsumgot)
+ return; /* error - invalid checksum */
+
+ CIMD_RX_ProcessRawPacket_parseparam_buf_s=CIMD_CatchBuffer +1/*START*/;
+ CIMD_RX_Packet_slots=1; /* just [0] now */
+ CIMD_RX_Packet_slot[0].code=0; /* ignored now - position */
+ CIMD_RX_Packet_slot[0].elf=CIMD_ELF_HexByte;
+ if (!CIMD_RX_ProcessRawPacket_parseparam(CIMD_RX_Packet_slot+0))
+ return; /* error - cmd code not parsed */
+
+ for (cmdp=CIMD_Cmd;cmdp<CIMD_Cmd+CIMD_Cmdcnt;cmdp++)
+ if (cmdp->code==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_s<CIMD_CatchBufferPtr;position++) {
+struct CIMD_paramslot *slot;
+
+ /* This code search is a crude overhead just for BIP now :-)
+ */
+ for (param=cmdp->param;param<cmdp->param+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,
+};