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 SMS centers by CIMD and related protococols.
15 See README-CIMD for more details on supported protocols.
17 The various routines are prefixed by CIMD.
20 Revision 1.1.1.1 2002/04/03 00:08:03 short
21 Found in "gnokii-working" directory, some November-patches version
28 /* System header files */
40 #include "win32/winserial.h"
45 #define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
46 #define sleep(x) Sleep((x) * 1000)
47 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
52 #define WRITEPHONE(a, b, c) device_write(b, c)
58 #include <sys/ioctl.h>
59 #include <sys/types.h>
64 #include "devices/unixserial.h"
68 /* Various header file */
72 #include "gsm-common.h"
73 #include "cfgreader.h"
75 /* Global variables used by code in gsm-api.c to expose the functions
76 supported by this model of phone. */
80 /* fd opened in device.c */
81 extern int device_portfd;
84 /* Our private defines */
86 /* Define if plain (non-Bin) Submit is needed for 7bit messages
88 #define CIMD_SUBMIT_7BIT 1
90 /* When now catchbuffer was provided and must have some space to decode
91 * OK/ERROR/... result codes.
93 #define CIMD_CATCHBUFFER_LENGTH 0x400
95 /* +1 of maximum position number of CIMD protocol parameter */
96 #define CIMD_PARAMSLOTS (0x10)
98 /* We assume the 'right one' class is 1 (Mobile Equipment specific) */
99 #define DEFAULT_CLASS 1
101 /* CIMD_Param_Nak_Error codes:
103 #define CIMD_NAK_NO_SMS (0x5001)
104 #define CIMD_NAK_KEEPALIVE_REPLY (0x9998)
105 #define CIMD_NAK_RESEND (0x9999)
107 /* Local variables */
110 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
112 static bool RequestTerminate;
115 static u8 CIMD_CatchBuffer[CIMD_CATCHBUFFER_LENGTH];
116 static u8 *CIMD_CatchBufferPtr=CIMD_CatchBuffer; /* current destination writing ptr */
117 static GSM_Error *CIMD_CatchBufferErrorP;
119 static void CIMD_CatchBufferStart(GSM_Error *errorcodep);
122 #define CIMD_MARK_START (0x02)
123 #define CIMD_MARK_STOP (0x03)
124 #define CIMD_MARK_SEP (0x09)
125 #define CIMD_MARK_SEPS "\x09"
127 /* ELF=ELement Format */
142 enum CIMD_Param_SAMPLE { /* we need some universal enum for typecasting all params */
143 CIMD_Param_SAMPLE_dummy,
148 enum CIMD_Param_Ack {
150 CIMD_Param_Ack_Error,
153 static const struct CIMD_Param CIMD_Param_BIP_Ack[]={
154 { /* CIMD_Param_Ack_Cmd */ 1,CIMD_ELF_HexByte },
155 { /* CIMD_Param_Ack_Error */ 2,CIMD_ELF_StringZ }, /* protocol version on Cmd_Login, otherwise 0x0000 */
160 enum CIMD_Param_Nak {
162 CIMD_Param_Nak_Error,
165 static const struct CIMD_Param CIMD_Param_BIP_Nak[]={
166 { /* CIMD_Param_Nak_Cmd */ 1,CIMD_ELF_HexByte },
167 { /* CIMD_Param_Nak_Error */ 2,CIMD_ELF_HexWord },
172 enum CIMD_Param_Login {
174 CIMD_Param_Login_PWD,
175 CIMD_Param_Login_NULL
177 static const struct CIMD_Param CIMD_Param_BIP_Login[]={
178 { /* CIMD_Param_Login_ID */ 1,CIMD_ELF_StringZ },
179 { /* CIMD_Param_Login_PWD */ 2,CIMD_ELF_StringZ },
184 enum CIMD_Param_Logout {
185 CIMD_Param_Logout_NULL
187 static const struct CIMD_Param CIMD_Param_BIP_Logout[]={
190 /* CIMD_Cmd_Retrieve:
192 enum CIMD_Param_Retrieve {
193 CIMD_Param_Retrieve_NULL
195 static const struct CIMD_Param CIMD_Param_BIP_Retrieve[]={
198 /* CIMD_Cmd_RetrieveReply:
200 enum CIMD_Param_RetrieveReply {
201 CIMD_Param_RetrieveReply_Destination,
202 CIMD_Param_RetrieveReply_SourceApplication, /* ??? */
203 CIMD_Param_RetrieveReply_Text,
204 CIMD_Param_RetrieveReply_Timestamp,
205 CIMD_Param_RetrieveReply_Is8bit, /* protocol version 1.14+, CIMD_Param_DCSEnable_Type_*DCS* reqd */
206 CIMD_Param_RetrieveReply_PID_DCS, /* protocol version 1.15+, CIMD_Param_DCSEnable_Type_*PID_DCS* reqd */
207 CIMD_Param_RetrieveReply_SPEC, /* protocol version 1.16+, CIMD_Param_DCSEnable_Type_*SPEC* reqd */
208 CIMD_Param_RetrieveReply_NULL
210 static const struct CIMD_Param CIMD_Param_BIP_RetrieveReply[]={
211 { /* CIMD_Param_RetrieveReply_Destination */ 1,CIMD_ELF_StringZ },
212 { /* CIMD_Param_RetrieveReply_SourceApplication */ 2,CIMD_ELF_StringZ },
213 { /* CIMD_Param_RetrieveReply_Text */ 3,CIMD_ELF_StringZ },
214 { /* CIMD_Param_RetrieveReply_Timestamp */ 4,CIMD_ELF_StringZ },
215 { /* CIMD_Param_RetrieveReply_Is8bit */ 5,CIMD_ELF_Decimal },
216 { /* CIMD_Param_RetrieveReply_PID_DCS */ 6,CIMD_ELF_Decimal }, /* upper=PID, lower=DCS, FIXME: should be HexWord! */
217 { /* CIMD_Param_RetrieveReply_SPEC */ 7,CIMD_ELF_Decimal }, /* FIXME: should be HexByte! */
222 enum CIMD_Param_Count {
223 CIMD_Param_Count_NULL
225 static const struct CIMD_Param CIMD_Param_BIP_Count[]={
228 /* CIMD_Cmd_CountReply:
230 enum CIMD_Param_CountReply {
231 CIMD_Param_CountReply_Used, /* Used==NotRead==Slots */
232 CIMD_Param_CountReply_NULL
234 static const struct CIMD_Param CIMD_Param_BIP_CountReply[]={
235 { /* CIMD_Param_CountReply_Used */ 1,CIMD_ELF_Decimal },
240 enum CIMD_Param_Submit {
241 CIMD_Param_Submit_Destination,
242 CIMD_Param_Submit_Text,
243 CIMD_Param_Submit_ValidityPeriod,
244 CIMD_Param_Submit_AUX, /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
245 CIMD_Param_Submit_DCS, /* protocol version 1.14+ */
246 CIMD_Param_Submit_PID, /* protocol version 1.15+ */
247 CIMD_Param_Submit_SPEC, /* protocol version 1.16+ */
248 CIMD_Param_Submit_NULL
250 static const struct CIMD_Param CIMD_Param_BIP_Submit[]={
251 { /* CIMD_Param_Submit_Destination */ 1,CIMD_ELF_StringZ },
252 { /* CIMD_Param_Submit_Text */ 2,CIMD_ELF_StringZ },
253 { /* CIMD_Param_Submit_ValidityPeriod */ 3,CIMD_ELF_HexByte },
254 { /* CIMD_Param_Submit_AUX */ 4,CIMD_ELF_Empty },
255 { /* CIMD_Param_Submit_DCS */ 5,CIMD_ELF_Decimal },
256 { /* CIMD_Param_Submit_PID */ 6,CIMD_ELF_HexByte },
257 { /* CIMD_Param_Submit_SPEC */ 7,CIMD_ELF_HexByte },
260 /* CIMD_Cmd_SubmitBin: protocol version 1.12+
262 enum CIMD_Param_SubmitBin {
263 CIMD_Param_SubmitBin_Destination,
264 CIMD_Param_SubmitBin_Text,
265 CIMD_Param_SubmitBin_ValidityPeriod,
266 CIMD_Param_SubmitBin_AUX, /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
267 CIMD_Param_SubmitBin_DCS, /* protocol version 1.14+ */
268 CIMD_Param_SubmitBin_PID, /* protocol version 1.15+ */
269 CIMD_Param_SubmitBin_SPEC, /* protocol version 1.16+ */
270 CIMD_Param_SubmitBin_NULL
272 static const struct CIMD_Param CIMD_Param_BIP_SubmitBin[]={
273 { /* CIMD_Param_SubmitBin_Destination */ 1,CIMD_ELF_StringZ },
274 { /* CIMD_Param_SubmitBin_Text */ 2,CIMD_ELF_StringZ },
275 { /* CIMD_Param_SubmitBin_ValidityPeriod */ 3,CIMD_ELF_HexByte },
276 { /* CIMD_Param_SubmitBin_AUX */ 4,CIMD_ELF_Empty },
277 { /* CIMD_Param_SubmitBin_DCS */ 5,CIMD_ELF_Decimal },
278 { /* CIMD_Param_SubmitBin_PID */ 6,CIMD_ELF_HexByte },
279 { /* CIMD_Param_SubmitBin_SPEC */ 7,CIMD_ELF_HexByte },
282 /* CIMD_Cmd_DCSEnable: protocol version 1.14+
284 enum CIMD_Param_DCSEnable {
285 CIMD_Param_DCSEnable_Type,
286 CIMD_Param_DCSEnable_NULL
288 static const struct CIMD_Param CIMD_Param_BIP_DCSEnable[]={
289 { /* CIMD_Param_DCSEnable_Type */ 1,CIMD_ELF_HexWord },
292 enum CIMD_Param_DCSEnable_Type {
293 CIMD_Param_DCSEnable_Type_None =0x0000, /* protocol version 1.14+ */
294 CIMD_Param_DCSEnable_Type_DCS =0x0001, /* protocol version 1.14+ */
295 CIMD_Param_DCSEnable_Type_PID_DCS =0x0002, /* protocol version 1.15+ */
296 CIMD_Param_DCSEnable_Type_PID_DCS_SPEC=0x0003, /* protocol version 1.16+ */
299 #define CIMD_PARAM_ENTRY(param) (param),ARRAY_LEN((param))
303 const struct CIMD_Param *param;
314 CIMD_Cmd_RetrieveReply,
317 CIMD_Cmd_SubmitBin, /* protocol version 1.12+ */
318 CIMD_Cmd_DCSEnable, /* protocol version 1.14+ */
319 CIMD_Cmd_NULL /* stdarg termination, MUST be last! */
321 static const struct CIMD_Cmd CIMD_Cmd_BIP[]={
322 { /* CIMD_Cmd_Ack */ 0x00,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Ack ) },
323 { /* CIMD_Cmd_Nak */ 0x99,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Nak ) },
324 { /* CIMD_Cmd_Login */ 0x01,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Login ) },
325 { /* CIMD_Cmd_Logout */ 0x02,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Logout ) },
326 { /* CIMD_Cmd_Submit */ 0x03,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Submit ) },
327 { /* CIMD_Cmd_Retrieve */ 0x05,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Retrieve ) },
328 { /* CIMD_Cmd_RetrieveReply */ 0x06,CIMD_PARAM_ENTRY(CIMD_Param_BIP_RetrieveReply) },
329 { /* CIMD_Cmd_Count */ 0x51,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Count ) },
330 { /* CIMD_Cmd_CountReply */ 0x61,CIMD_PARAM_ENTRY(CIMD_Param_BIP_CountReply ) },
331 { /* CIMD_Cmd_SubmitBin */ 0x31,CIMD_PARAM_ENTRY(CIMD_Param_BIP_SubmitBin ) },
332 { /* CIMD_Cmd_DCSEnable */ 0x53,CIMD_PARAM_ENTRY(CIMD_Param_BIP_DCSEnable ) },
335 static const struct CIMD_Cmd *CIMD_Cmd=CIMD_Cmd_BIP; /* FIXME: When other protocols get supported... */
336 static unsigned CIMD_Cmdcnt=ARRAY_LEN(CIMD_Cmd_BIP); /* FIXME: When other protocols get supported... */
338 struct CIMD_Param_Ack_Nak_Error {
343 static const struct CIMD_Param_Ack_Nak_Error CIMD_Param_Ack_Nak_Error[]={
345 { 0x1101,N_("User is already logged in") },
346 { 0x1001,N_("Logging in is currently disabled") },
347 { 0x9999,N_("Username not found") },
348 /* 0x000? can be bitmask combined from: */
349 { 0x0001,N_("Invalid password") },
350 { 0x0002,N_("User has forbidden access") },
351 { 0x0008,N_("User is already registered") }, /* ??? difference from 0x1101 ? */
352 /* CIMD_Cmd_Retrieve / CIMD_Cmd_Count */
353 { 0x5001,N_("No SMS found or not detectable") }, /* ==CIMD_NAK_NO_SMS */
354 { 0x5011,N_("Function not enabled") },
355 /* CIMD_Cmd_Retrieve */
356 { 0x5021,N_("Automatical retrieve is active") },
357 /* CIMD_Cmd_Submit / CIMD_Cmd_SubmitBin */
358 { 0x3001,N_("Error during processing SMS in BMG") },
359 { 0x3061,N_("Destination number has invalid format") },
360 /* CIMD_Cmd_SubmitBin */
361 { 0x3051,N_("Invalid DCS") }, /* protocol version 1.14+ */
362 { 0x3081,N_("Invalid Text") }, /* protocol version 1.14+ */
363 { 0x3091,N_("SMS is empty") }, /* protocol version 1.14+ */
364 { 0x3099,N_("SMS submit too fast") }, /* protocol version 1.16+ */
368 static struct CIMD_paramslot CIMD_RX_Packet_slot[CIMD_PARAMSLOTS];
369 #define CIMD_RX_PACKET_PARAMSLOT(paramsample) (CIMD_RX_Packet_slot+1+(unsigned)(paramsample))
370 static enum CIMD_Cmd_Type CIMD_RX_Packet_Cmd;
371 static unsigned CIMD_RX_Packet_slots;
374 /* RETURNS: Processed */
375 typedef bool (*CIMD_RX_PatrolFunc)(void);
376 typedef void (*CIMD_RX_PatrolReset)(void);
378 struct CIMD_RX_Patrol {
379 enum CIMD_Cmd_Type cmd;
380 CIMD_RX_PatrolFunc func;
381 CIMD_RX_PatrolReset reset;
384 static const struct CIMD_RX_Patrol *CIMD_RX_Patrol_Current;
386 static char *CIMD_RX_Patrol_Ack_Error;
387 static u16 CIMD_RX_Patrol_Nak_Error;
392 static pthread_t Thread;
394 static pthread_t selThread;
399 /* Local variables used by get/set phonebook entry code. Buffer is used as a
400 source or destination for phonebook data and other functions... Error is
401 set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
402 handler routines as appropriate. */
404 static GSM_SMSMessage *CurrentSMSMessage;
405 static GSM_Error CurrentSMSMessageError;
407 static GSM_SMSStatus *CurrentSMSStatus;
408 static GSM_Error CurrentSMSStatusError;
410 static unsigned char Revision[GSM_MAX_REVISION_LENGTH];
411 static unsigned char Model [GSM_MAX_MODEL_LENGTH];
414 static u8 CIMD_TX_SendCommand_buf[sizeof(CIMD_CatchBuffer)]; /* sizeof() is not required to be == but it is appropriate */
415 static u8 *CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
417 struct CIMD_paramslot {
421 struct { u8 i; } HexByte;
422 struct { u16 i; } HexWord;
423 struct { int i; } Decimal;
424 struct { char *s; } StringZ;
425 struct { u8 *buf; size_t len; } HexBlock;
426 struct { int _dummy; } Empty;
430 /* slots are sorted by moving the whole blocks but they are small so it is OK
432 static struct CIMD_paramslot CIMD_TX_SendCommand_paramslots[CIMD_PARAMSLOTS];
433 static int CIMD_TX_SendCommand_paramslots_full;
434 static enum CIMD_Cmd_Type CIMD_TX_SendCommand_Cmd;
438 static bool CIMD_TX_SendCommand_vpushparam(u16 code,enum CIMD_ELF elf,va_list *app)
440 struct CIMD_paramslot *slot=CIMD_TX_SendCommand_paramslots+(CIMD_TX_SendCommand_paramslots_full++);
442 if (slot>=CIMD_TX_SendCommand_paramslots+ARRAY_LEN(CIMD_TX_SendCommand_paramslots)) {
444 fprintf(stderr,"Out of param slots!\n");
450 case CIMD_ELF_HexByte:
451 slot->u.HexByte.i=va_arg((*app),int);
453 case CIMD_ELF_HexWord:
454 slot->u.HexWord.i=va_arg((*app),int);
456 case CIMD_ELF_Decimal:
457 slot->u.Decimal.i=va_arg((*app),int);
459 case CIMD_ELF_StringZ:
460 slot->u.StringZ.s=va_arg((*app),char *);
462 case CIMD_ELF_HexBlock:
463 slot->u.HexBlock.buf=va_arg((*app),u8 *);
464 slot->u.HexBlock.len=va_arg((*app),size_t);
474 static bool CIMD_TX_SendCommand_pushparam(u16 code,enum CIMD_ELF elf,...)
480 r=CIMD_TX_SendCommand_vpushparam(code,elf,&ap);
485 static int CIMD_TX_SendCommand_paramslots_compare
486 (const struct CIMD_paramslot *a,const struct CIMD_paramslot *b)
488 return((b->code<a->code)-(a->code<b->code));
493 static bool CIMD_TX_SendCommand_storeparam(struct CIMD_paramslot *slot)
496 case CIMD_ELF_HexByte:
497 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",slot->u.HexByte.i);
499 case CIMD_ELF_HexWord:
500 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%04X",slot->u.HexWord.i);
502 case CIMD_ELF_Decimal:
503 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%d",slot->u.Decimal.i);
505 case CIMD_ELF_StringZ: {
507 if (!slot->u.StringZ.s) /* NULL is interpreted as "" */
509 len=strlen(slot->u.StringZ.s);
510 if (CIMD_TX_SendCommand_buf_d+len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
511 return(false); /* error - overflow */
512 memcpy(CIMD_TX_SendCommand_buf_d,slot->u.StringZ.s,len);
513 CIMD_TX_SendCommand_buf_d+=len;
515 case CIMD_ELF_HexBlock:
516 if (CIMD_TX_SendCommand_buf_d+2*slot->u.HexBlock.len
517 > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
518 return(false); /* error - overflow */
519 CIMD_TX_SendCommand_buf_d=SMS_BlockToHex(CIMD_TX_SendCommand_buf_d,
520 slot->u.HexBlock.buf,slot->u.HexBlock.len); /* never fails */
525 return(true); /* success */
528 static unsigned CIMD_KeepAlives=0;
529 static pthread_mutex_t CIMD_KeepAlivesLock;
531 /* This function is NOT thread-safe!
533 static void CIMD_KeepAlivesLockInit(void)
535 static bool done=false;
539 pthread_mutex_init(&CIMD_KeepAlivesLock,NULL);
543 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...);
545 static void CIMD_KeepAlivesCheck(void)
549 CIMD_KeepAlivesLockInit();
550 pthread_mutex_lock(&CIMD_KeepAlivesLock);
551 alives=CIMD_KeepAlives;
553 pthread_mutex_unlock(&CIMD_KeepAlivesLock);
556 CIMD_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,
558 CIMD_Param_Nak_Cmd,(CIMD_Cmd+(unsigned)CIMD_Cmd_Ack)->code, /* ==0x00 */
559 CIMD_Param_Nak_Error,CIMD_NAK_KEEPALIVE_REPLY, /* ==0x9998 */
560 CIMD_Param_Nak_NULL);
567 static bool CIMD_TX_SendCommandResend(boid)
571 writephone_got=WRITEPHONE(PortFD, CIMD_TX_SendCommand_buf, CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf);
574 write(1,"CMD:<STX>",9);
575 write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1));
576 write(1,"<ETX>\n",6);
579 return(writephone_got==(CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf));
582 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...)
585 const struct CIMD_Cmd *cmdstruct;
586 enum CIMD_Param_SAMPLE param;
587 const struct CIMD_Param *paramstruct;
589 struct CIMD_paramslot *slot;
592 CIMD_KeepAlivesCheck();
597 CIMD_RX_Patrol_Current=patrol;
599 CIMD_TX_SendCommand_Cmd=cmd;
600 CIMD_TX_SendCommand_paramslots_full=0;
601 if ((unsigned)cmd>=CIMD_Cmdcnt)
602 goto fail; /* assert */
603 cmdstruct=CIMD_Cmd+(unsigned)cmd;
605 if (!CIMD_TX_SendCommand_pushparam(0/*code*/,CIMD_ELF_HexByte/*elf*/,cmdstruct->code))
608 param=va_arg(ap,enum CIMD_Param_SAMPLE);
609 if ((unsigned)param==cmdstruct->paramcnt)
610 break; /* CIMD_Param_*_NULL reached */
611 if ((unsigned)param>cmdstruct->paramcnt)
612 goto fail; /* assert */
613 paramstruct=(cmdstruct->param+(unsigned)param);
614 if (!CIMD_TX_SendCommand_vpushparam(paramstruct->code,paramstruct->elf,&ap))
617 qsort(CIMD_TX_SendCommand_paramslots,CIMD_TX_SendCommand_paramslots_full,
618 sizeof(*CIMD_TX_SendCommand_paramslots),
619 (int (*)(const void *,const void *))CIMD_TX_SendCommand_paramslots_compare);
621 CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
622 *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_START;
624 slot=CIMD_TX_SendCommand_paramslots;
625 for (parami=0;slot<CIMD_TX_SendCommand_paramslots+CIMD_TX_SendCommand_paramslots_full;parami++) {
626 if (CIMD_TX_SendCommand_buf_d +32/*safety*/ > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) {
627 /* error - overflow */
630 *errorcodep=GE_INTERNALERROR;
633 if (slot->code==parami) {
634 if (!CIMD_TX_SendCommand_storeparam(slot)) /* error */
637 if (slot->code>=parami)
638 *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_SEP;
639 if (slot->code<=parami)
643 for (u8s=CIMD_TX_SendCommand_buf;u8s<CIMD_TX_SendCommand_buf_d;u8s++)
645 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",(unsigned)xsum);
646 *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_STOP;
648 CIMD_CatchBufferStart(errorcodep);
650 if (!CIMD_TX_SendCommandResend())
653 /* success but we don't wait for the result code */
656 /* This function is used to get storage status from the phone. It currently
657 supports two different memory areas - internal and SIM. */
660 wait_on(volatile GSM_Error *what, int timeout)
662 GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
664 while (timeout && ((r=*what)==GE_BUSY)) {
669 CIMD_KeepAlivesCheck();
672 /* any specific patrollers are no longer valid */
673 CIMD_RX_Patrol_Current=NULL;
676 printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
682 #define WAIT_ON(what, timeout) \
684 GSM_Error res = wait_on(what, timeout); \
685 if (res != GE_NONE) \
689 /* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
691 #define CIMD_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
693 GSM_Error _CIMD_TX_SENDCOMMAND_WAIT_ON_err,*_CIMD_TX_SENDCOMMAND_WAIT_ON_errp; \
695 if (!(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
696 _CIMD_TX_SENDCOMMAND_WAIT_ON_errp=&_CIMD_TX_SENDCOMMAND_WAIT_ON_err; \
697 CIMD_TX_SendCommand(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
698 WAIT_ON(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
702 #define CIMD_ERR_WRAPPER(expr) \
704 GSM_Error err=(expr); \
709 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
711 static unsigned char CIMD_SMStoSPEC_BIP_CIMD(GSM_SMSMessage *SMS)
714 /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
716 |((!!SMS->ReplyViaSameSMSC)<<1) /*ReplyPath*/
717 |((!!SMS->UDHPresent)<<2) /*UDH set*/
721 static unsigned char CIMD_SMStoDCS(GSM_SMSMessage *SMS)
723 int class=(SMS->Class==-1 ? DEFAULT_CLASS : SMS->Class);
725 #if 0 /* NEVER send 0 for BIP CIMD as it would translate it to F6 !!!
727 if (!SMS->EightBit && class==DEFAULT_CLASS)
730 return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
733 static GSM_Error CIMD_PhoneSetup(void)
736 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,100/*timeout*/,NULL/*patrol*/,
738 /* NULLs will be interpreted as ""
740 CIMD_Param_Login_ID,CFG_Get(CFG_Info,"CIMD","name"),
741 CIMD_Param_Login_PWD,CFG_Get(CFG_Info,"CIMD","password"),
742 CIMD_Param_Login_NULL);
743 if (!CIMD_RX_Patrol_Ack_Error)
746 SAFE_STRNCPY_SIZEOF(Revision,CIMD_RX_Patrol_Ack_Error);
748 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
750 CIMD_Param_DCSEnable_Type,(u16)CIMD_Param_DCSEnable_Type_PID_DCS_SPEC,
751 CIMD_Param_DCSEnable_NULL);
758 static void CIMD_RX_Char(char rx_byte);
759 static void CIMD_SigHandler(int status);
760 static bool CIMD_OpenSerial(void);
762 GSM_Phone phone_cimd; /* forward declaration */
764 /* Initialise variables and state machine. */
766 static GSM_Error CIMD_Init(GSM_Data *data, GSM_Statemachine *state)
768 RequestTerminate = false;
770 SAFE_STRNCPY_SIZEOF(Model,data->Model);
772 /* Create and start main thread. */
777 rtn = ! OpenConnection(State->Link.PortDevice,CIMD_RX_Char,CIMD_KeepAliveProc);
779 return(GE_INTERNALERROR);
782 SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
783 if (!CIMD_OpenSerial())
784 return(GE_INTERNALERROR);
787 CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
793 /* thread for handling incoming data */
794 void CIMD_SelectLoop()
798 struct timeval timeout;
801 FD_SET(device_portfd, &readfds);
802 /* set timeout to 15 seconds */
805 while (!RequestTerminate) {
806 err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
807 /* call singal handler to process incoming data */
808 if ( err > 0 ) CIMD_SigHandler(0);
809 else if (err == -1) perror("Error in SelectLoop");
814 /* Applications should call CIMD_Terminate to shut down the CIMD thread and
815 close the serial port. */
817 static GSM_Error CIMD_Terminate(GSM_Data *data, GSM_Statemachine *state)
822 /* Don't wait too much as we can have already broken link
824 CIMD_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
826 CIMD_Param_Logout_NULL);
827 wait_on(&err,20/*timeout*/); /* errors ignored, of course */
830 /* Request termination of thread */
831 RequestTerminate = true;
834 /* Now wait for thread to terminate. */
835 pthread_join(Thread, NULL);
837 /* Close serial port. */
847 /* messagecenter->No" is NOT set as it is input argument */
848 static void CIMD_MessageCenterClear(GSM_MessageCenter *messagecenter)
850 messagecenter->Name[0]='\0'; /* not present up to Nokia 9110i */
851 messagecenter->Recipient[0]='\0'; /* not present up to Nokia 9110i */
852 messagecenter->Number[0]='\0'; /* default */
853 messagecenter->Format=GSMF_Text; /* default */
854 messagecenter->Validity=GSMV_72_Hours; /* default */
857 static bool CIMD_RX_Patrol_CountReply(void)
859 CurrentSMSStatus->UnRead=/*FALLTHRU*/
860 CurrentSMSStatus->Used =/*FALLTHRU*/
861 CurrentSMSStatus->Slots =/*FALLTHRU*/
862 CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_CountReply_Used)->u.Decimal.i;
864 CurrentSMSStatusError=GE_NONE;
868 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_CountReply_struct=
869 { CIMD_Cmd_CountReply,CIMD_RX_Patrol_CountReply };
871 static GSM_Error CIMD_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
873 CurrentSMSStatus = data->SMSStatus;
874 CurrentSMSStatus->UnRead=0; /* default */
875 CurrentSMSStatus->Used =0; /* default */
876 CurrentSMSStatus->Slots =0; /* default */
878 CIMD_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,100/*timeout*/,&CIMD_RX_Patrol_CountReply_struct/*patrol*/,
880 CIMD_Param_Count_NULL);
886 static GSM_Error CIMD_GetImei(GSM_Data *data, GSM_Statemachine *state)
888 /* not supported by the protocol */
893 static GSM_Error CIMD_GetRevision(GSM_Data *data, GSM_Statemachine *state)
896 strcpy(data->Revision,Revision);
898 } else return (GE_TRYAGAIN);
901 static GSM_Error CIMD_GetModel(GSM_Data *data, GSM_Statemachine *state)
903 /* not supported by the protocol */
904 strcpy(data->Model,Model);
908 static GSM_Error CIMD_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
910 /* not supported by the protocol */
915 static void CIMD_DateTimeSetCurrent(GSM_DateTime *datetime)
917 time_t current=time(NULL);
918 struct tm *tm=localtime(¤t);
920 datetime->AlarmEnabled=false;
922 datetime->Year =tm->tm_year+1900;
923 datetime->Month =tm->tm_mon+1;
924 datetime->Day =tm->tm_mday;
925 datetime->Hour =tm->tm_hour;
926 datetime->Minute=tm->tm_min;
927 datetime->Second=tm->tm_sec;
929 datetime->Timezone=timezone;
932 /* scts=="18.06.1998 22:33:23"
934 static bool CIMD_SCTStoSMS(GSM_SMSMessage *SMS,const char *scts)
936 GSM_DateTime *DateTime=&SMS->Time;
937 const char *fmt="%2d.%2d.%4d %2d:%2d:%2d"; /* trailing '\0' IS used! */
944 int nums=(*++fs)-'0';
952 /* string is completely valid now */
961 DateTime->Timezone=0;
966 static void CIMD_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
968 switch ((dcs&0xF0)>>4) {
972 CurrentSMSMessage->EightBit=false;
977 CurrentSMSMessage->EightBit=!!(dcs&0x04); /* bit 2 */
978 CurrentSMSMessage->Class=(dcs&0x03); /* bits 0 & 1 */
983 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
985 static void CIMD_SPEC_BIP_CIMDtoSMS(GSM_SMSMessage *SMS,u8 spec)
987 /* Such specification not supported by BIP:
989 CurrentSMSMessage->Type=GST_MT;
990 CurrentSMSMessage->Status=GSS_NOTSENTREAD;
992 /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
995 SMS->UDHPresent=!!(spec&(1<<2));
997 SMS->ReplyViaSameSMSC=!!(spec&(1<<1));
1000 static bool CIMD_RX_Patrol_RetrieveReply(void)
1002 u16 pid_dcs; /* upper=PID, lower=DCS */
1005 const char *destination=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Destination)->u.StringZ.s;
1007 if (strlen(destination)+1 > sizeof(CurrentSMSMessage->Sender)) {
1009 CurrentSMSMessageError=GE_INTERNALERROR;
1010 return(true); /* error */
1012 strcpy(CurrentSMSMessage->Sender,destination);
1015 if ((CurrentSMSMessage->EightBit=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Is8bit)->u.Decimal.i)) {
1016 const char *hextext=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1017 size_t hextextlen=strlen(hextext);
1019 if ((hextextlen&1) || (hextextlen/2 > sizeof(CurrentSMSMessage->MessageText)))
1020 goto fail; /* error - message too long */
1021 if (!SMS_BlockFromHex(CurrentSMSMessage->MessageText/*d*/,hextext,hextextlen))
1022 goto fail; /* error - parse error */
1023 CurrentSMSMessage->MessageTextLength=hextextlen/2;
1026 const char *text=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1027 size_t textlen=strlen(text);
1029 if (textlen+1 > sizeof(CurrentSMSMessage->MessageText))
1030 goto fail; /* error - message too long */
1031 strcpy(CurrentSMSMessage->MessageText,text);
1032 CurrentSMSMessage->MessageTextLength=textlen;
1035 /* errors ignored as it is not fatal */
1036 CIMD_SCTStoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Timestamp)->u.StringZ.s);
1038 pid_dcs=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_PID_DCS)->u.HexWord.i;
1040 /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
1041 CurrentSMSMessage->MessageCenter.Format=(GSM_SMSMessageFormat)(pid_dcs>>8U); /* <pid> */
1042 CIMD_DCStoSMS(CurrentSMSMessage,(pid_dcs&0x00FFU));
1043 CIMD_SPEC_BIP_CIMDtoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_SPEC)->u.HexByte.i);
1045 if (CurrentSMSMessage->UDHPresent) {
1047 if (CurrentSMSMessage->MessageTextLength<=0)
1049 udhlen=1/*sizeof(udhlen)*/ +CurrentSMSMessage->MessageText[0];
1050 if (udhlen > CurrentSMSMessage->MessageTextLength)
1052 memcpy(CurrentSMSMessage->UDH,CurrentSMSMessage->MessageText,udhlen);
1053 memmove(CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageText+udhlen,
1054 CurrentSMSMessage->MessageTextLength-udhlen +1/*Trailing '\0'*/);
1055 CurrentSMSMessage->MessageTextLength-=udhlen;
1058 CurrentSMSMessageError=GE_NONE;
1062 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_RetrieveReply_struct=
1063 { CIMD_Cmd_RetrieveReply,CIMD_RX_Patrol_RetrieveReply };
1065 static GSM_Error CIMD_GetSMS(GSM_Data *data, GSM_Statemachine *state)
1067 CurrentSMSMessage = data->SMSMessage;
1069 if (CurrentSMSMessage->MemoryType!=GMT_SM)
1070 return(GE_INVALIDMEMORYTYPE);
1072 CIMD_DateTimeSetCurrent(&CurrentSMSMessage->Time); /* default */
1073 CIMD_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime); /* not present in the protocol */
1074 CurrentSMSMessage->MessageTextLength=0; /* default */
1075 CurrentSMSMessage->Validity=72/*hours*/*60; /* default */
1076 CurrentSMSMessage->UDHPresent=false; /* default */
1077 CurrentSMSMessage->MessageText[0]='\0'; /* default */
1079 CIMD_MessageCenterClear(&CurrentSMSMessage->MessageCenter); /* default */
1080 CurrentSMSMessage->MessageCenter.No=0; /* default - input for GetSMSCenter */
1082 CurrentSMSMessage->Sender[0]='\0'; /* default */
1083 CurrentSMSMessage->Destination[0]='\0'; /* default */
1084 CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location; /* default */
1085 /* CurrentSMSMessage->MemoryType is input argument */
1086 CurrentSMSMessage->Type=GST_UN; /* default, detection of EMPTY SMSes! */
1087 CurrentSMSMessage->Status=GSS_SENTREAD; /* default */
1088 CurrentSMSMessage->Class=DEFAULT_CLASS; /* default */
1089 CurrentSMSMessage->EightBit=false; /* default */
1090 CurrentSMSMessage->Compression=false; /* default */
1091 /* CurrentSMSMessage->Location is input argument */
1092 CurrentSMSMessage->ReplyViaSameSMSC=false; /* default */
1094 CIMD_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&CIMD_RX_Patrol_RetrieveReply_struct/*patrol*/,
1096 CIMD_Param_Retrieve_NULL);
1097 if (GE_NONE!=wait_on(&CurrentSMSMessageError,90/*timeout*/)) {
1098 if (CIMD_RX_Patrol_Nak_Error!=CIMD_NAK_NO_SMS)
1099 return(CurrentSMSMessageError);
1100 /* We don't return GE_EMPTYSMSLOCATION as when we have already eaten
1101 * all the wating SMSes there cannot be any other one
1103 return(GE_INVALIDSMSLOCATION);
1109 static GSM_Error CIMD_SendSMS(GSM_Data *data, GSM_Statemachine *state)
1111 u8 bintext[sizeof(CurrentSMSMessage->UDH)+sizeof(CurrentSMSMessage->MessageText)],*d;
1112 char hexbintext[sizeof(bintext) +1/*Trailing '\0'*/];
1114 CurrentSMSMessage = data->SMSMessage;
1116 CurrentSMSMessage->MessageNumber=0; /* default */
1119 if (CurrentSMSMessage->UDHPresent) {
1120 size_t UDHlen=1+CurrentSMSMessage->UDH[0];
1122 memcpy(d,CurrentSMSMessage->UDH,UDHlen);
1124 #ifdef CIMD_SUBMIT_7BIT
1125 if (!CurrentSMSMessage->EightBit) /* We are not able to send UDH by 7bit channel */
1126 return(GE_NOTSUPPORTED);
1130 #ifdef CIMD_SUBMIT_7BIT
1131 1 /* Never do 7bit->8bit encoding when CIMD_SUBMIT_7BIT */
1133 CurrentSMSMessage->EightBit
1136 memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
1137 d+=CurrentSMSMessage->MessageTextLength;
1139 #ifndef CIMD_SUBMIT_7BIT
1141 size_t byteslen=PackSevenBitsToEight(
1142 /* check it out yourself, really the number of used bits for UDH header on the start
1143 * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
1146 CurrentSMSMessage->MessageText,d);
1152 #ifdef CIMD_SUBMIT_7BIT
1153 CurrentSMSMessage->EightBit
1155 1 /* Never use plain Submit for 8bit messages */
1158 *(SMS_BlockToHex(hexbintext,bintext,(d-bintext)/*len*/))='\0';
1160 /* DANGER: Keep in sync with CIMD_Cmd_Submit !!!
1162 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1164 CIMD_Param_SubmitBin_Destination,CurrentSMSMessage->Destination,
1165 CIMD_Param_SubmitBin_Text,hexbintext,
1166 CIMD_Param_SubmitBin_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1167 CIMD_Param_SubmitBin_AUX, /* Empty */
1168 CIMD_Param_SubmitBin_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1169 CIMD_Param_SubmitBin_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1170 CIMD_Param_SubmitBin_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1171 CIMD_Param_SubmitBin_NULL);
1173 #ifdef CIMD_SUBMIT_7BIT
1177 /* DANGER: Keep in sync with CIMD_Cmd_SubmitBin !!!
1179 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1181 CIMD_Param_Submit_Destination,CurrentSMSMessage->Destination,
1182 CIMD_Param_Submit_Text,bintext,
1183 CIMD_Param_Submit_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1184 CIMD_Param_Submit_AUX, /* Empty */
1185 CIMD_Param_Submit_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1186 CIMD_Param_Submit_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1187 CIMD_Param_Submit_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1188 CIMD_Param_Submit_NULL);
1192 return(GE_SMSSENDOK);
1195 static GSM_Error CIMD_Reset(GSM_Data *data, GSM_Statemachine *state)
1197 CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
1204 /* Called by initialisation code to open comm port in asynchronous mode. */
1206 static bool CIMD_OpenSerial(void)
1213 struct sigaction sig_io;
1215 /* Set up and install handler before enabling async IO on port. */
1217 sig_io.sa_handler = CIMD_SigHandler;
1218 sig_io.sa_flags = 0;
1219 sigaction (SIGIO, &sig_io, NULL);
1224 result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, GCT_Serial);
1227 perror(_("Couldn't open CIMD device"));
1232 /* create a thread to handle incoming data from mobile phone */
1233 rtn = pthread_create(&selThread, NULL, (void*)CIMD_SelectLoop, (void*)NULL);
1234 if (rtn != 0) return false;
1237 /* device_changespeed() not needed as device_open() now automatically
1238 * sets the user-specified (or default) speed.
1243 static void CIMD_SigHandler(int status)
1245 unsigned char buffer[255];
1247 res = device_read(buffer, 255);
1248 for (count = 0; count < res ; count ++)
1249 CIMD_RX_Char(buffer[count]);
1253 static bool CIMD_RX_Patrol_Ack(void)
1255 if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i) { /* ==0x99 */
1256 /* We cannot send the keepalive here as some dangerous operation may be in progress
1258 CIMD_KeepAlivesLockInit();
1259 pthread_mutex_lock(&CIMD_KeepAlivesLock);
1261 pthread_mutex_unlock(&CIMD_KeepAlivesLock);
1265 if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1266 return(false); /* Ack for some unknown command */
1268 free(CIMD_RX_Patrol_Ack_Error);
1269 CIMD_RX_Patrol_Ack_Error=strdup(CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Error)->u.StringZ.s);
1271 /* Some patrol may have already indicated some error!
1273 if (CIMD_CatchBufferErrorP && *CIMD_CatchBufferErrorP==GE_BUSY)
1274 *CIMD_CatchBufferErrorP=GE_NONE;
1279 static void CIMD_RX_Patrol_Ack_reset(void)
1281 free(CIMD_RX_Patrol_Ack_Error);
1282 CIMD_RX_Patrol_Ack_Error=NULL;
1285 static const char *CIMD_RX_Patrol_Nak_resolve(u16 errorcode)
1287 const struct CIMD_Param_Ack_Nak_Error *errorp;
1289 for (errorp=CIMD_Param_Ack_Nak_Error;errorp<CIMD_Param_Ack_Nak_Error+ARRAY_LEN(CIMD_Param_Ack_Nak_Error);errorp++)
1290 if (errorcode==errorp->code)
1291 return(_(errorp->msg));
1292 return(_("Unknown error code"));
1295 static void CIMD_RX_Patrol_Nak_dump(u16 errorcode)
1297 fprintf(stderr,_("Got CIMD error code 0x%04X: "),errorcode);
1298 if (!(errorcode & ~0x000F)) { /* combined error code */
1300 fprintf(stderr,_("combined:"));
1301 for (errormask=0x0001;errormask<=errorcode;errormask<<=1)
1302 if (errorcode&errormask)
1303 fprintf(stderr," +%s",CIMD_RX_Patrol_Nak_resolve(errormask));
1307 fprintf(stderr,"%s\n",CIMD_RX_Patrol_Nak_resolve(errorcode));
1310 static bool CIMD_RX_Patrol_Nak(void)
1312 if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i /* ==0x99 */
1313 && CIMD_NAK_RESEND == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i) { /* 0x9999 */
1314 fprintf(stderr,_("WARNING: Requested to resend last packet!!\n"));
1315 CIMD_TX_SendCommandResend();
1319 if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1320 return(false); /* Nak for some unknown command */
1322 CIMD_RX_Patrol_Nak_Error=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i;
1323 if (CIMD_CatchBufferErrorP)
1324 *CIMD_CatchBufferErrorP=GE_INTERNALERROR;
1326 CIMD_RX_Patrol_Nak_dump(CIMD_RX_Patrol_Nak_Error);
1331 static void CIMD_RX_Patrol_Nak_reset(void)
1333 CIMD_RX_Patrol_Nak_Error=0;
1336 static const struct CIMD_RX_Patrol CIMD_RX_Patrols[]={
1337 { CIMD_Cmd_Ack,CIMD_RX_Patrol_Ack,CIMD_RX_Patrol_Ack_reset },
1338 { CIMD_Cmd_Nak,CIMD_RX_Patrol_Nak,CIMD_RX_Patrol_Nak_reset },
1342 static void CIMD_CatchBufferReset(void)
1344 CIMD_CatchBufferPtr=CIMD_CatchBuffer;
1347 static void CIMD_CatchBufferStart(GSM_Error *errorcodep)
1349 static GSM_Error err_trashcan;
1350 const struct CIMD_RX_Patrol *patrol;
1353 errorcodep=&err_trashcan;
1354 CIMD_CatchBufferErrorP=errorcodep;
1356 CIMD_CatchBufferReset();
1358 for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1363 static u8 *CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1367 static bool CIMD_RX_ProcessRawPacket_parseparam(struct CIMD_paramslot *slot)
1369 switch (slot->elf) {
1371 case CIMD_ELF_HexByte: {
1372 unsigned parsedbyte;
1373 if (0 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1374 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1375 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[2]
1378 if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedbyte))
1380 slot->u.HexByte.i=parsedbyte;
1381 CIMD_RX_ProcessRawPacket_parseparam_buf_s+=2+1;
1384 case CIMD_ELF_HexWord: {
1385 unsigned parsedword;
1386 if (0 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1387 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1388 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[2])
1389 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[3])
1390 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[4]
1393 if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedword))
1395 slot->u.HexWord.i=parsedword;
1396 CIMD_RX_ProcessRawPacket_parseparam_buf_s+=4+1;
1399 case CIMD_ELF_Decimal: {
1401 u8 *start=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1402 if (CIMD_MARK_SEP==*start)
1403 return(false); /* empty Decimal not permitted */
1404 while (isdigit(*CIMD_RX_ProcessRawPacket_parseparam_buf_s))
1405 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1406 if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++) /* skip CIMD_MARK_SEP */
1408 if (1!=sscanf(start,"%d" CIMD_MARK_SEPS,&parsedint))
1410 slot->u.Decimal.i=parsedint;
1413 case CIMD_ELF_StringZ:
1414 slot->u.StringZ.s=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1415 while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1416 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1417 *CIMD_RX_ProcessRawPacket_parseparam_buf_s++='\0'; /* skip CIMD_MARK_SEP */
1420 case CIMD_ELF_HexBlock:
1421 slot->u.HexBlock.buf=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1423 while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1424 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1425 if ((CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf)&1)
1426 return(false); /* odd number of xdigits */
1428 slot->u.HexBlock.len=(CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf);
1429 if (!SMS_BlockFromHex(slot->u.HexBlock.buf/*d*/,slot->u.HexBlock.buf/*s*/,slot->u.HexBlock.len*2))
1430 return(false); /* parse error */
1432 CIMD_RX_ProcessRawPacket_parseparam_buf_s++; /* skip CIMD_MARK_SEP */
1435 case CIMD_ELF_Empty:
1436 if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1437 return(false); /* some content found */
1440 return(true); /* success */
1444 static bool CIMD_RX_EvalPatrol(const struct CIMD_RX_Patrol *patrol)
1447 return(false); /* not recognized */
1448 if (CIMD_RX_Packet_Cmd!=patrol->cmd)
1449 return(false); /* not recognized */
1450 return((*patrol->func)());
1453 static inline void CIMD_RX_ProcessPacket(void)
1455 const struct CIMD_RX_Patrol *patrol;
1457 for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1458 if (CIMD_RX_EvalPatrol(patrol))
1459 return; /* recognized and processed - whether successfuly is not interesting */
1460 CIMD_RX_EvalPatrol(CIMD_RX_Patrol_Current);
1463 static inline void CIMD_RX_ProcessRawPacket(void)
1468 const struct CIMD_Param *param;
1469 const struct CIMD_Cmd *cmdp;
1472 write(1,"GOT:<STX>",9);
1473 write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1));
1474 write(1,"<ETX>\n",6);
1477 if (CIMD_CatchBufferPtr<CIMD_CatchBuffer +1/*START*/ +1/*SEP*/ +2/*xsum*/ +1/*STOP*/)
1478 return; /* error - buffer too short */
1479 if (0 || CIMD_CatchBufferPtr[-4]!=CIMD_MARK_SEP
1480 || !isxdigit(CIMD_CatchBufferPtr[-3])
1481 || !isxdigit(CIMD_CatchBufferPtr[-2])
1483 return; /* error - invalid buffer tail */
1485 /* (*Ptr) will now point to the end of parameter part */
1486 CIMD_CatchBufferPtr-=3;
1487 if (1!=sscanf(CIMD_CatchBufferPtr,"%X" CIMD_MARK_SEPS,&xsumgot))
1488 return; /* INTERNAL - we have already checked the validity! */
1490 for (u8s=CIMD_CatchBuffer;u8s<CIMD_CatchBufferPtr;u8s++)
1493 return; /* error - invalid checksum */
1495 CIMD_RX_ProcessRawPacket_parseparam_buf_s=CIMD_CatchBuffer +1/*START*/;
1496 CIMD_RX_Packet_slots=1; /* just [0] now */
1497 CIMD_RX_Packet_slot[0].code=0; /* ignored now - position */
1498 CIMD_RX_Packet_slot[0].elf=CIMD_ELF_HexByte;
1499 if (!CIMD_RX_ProcessRawPacket_parseparam(CIMD_RX_Packet_slot+0))
1500 return; /* error - cmd code not parsed */
1502 for (cmdp=CIMD_Cmd;cmdp<CIMD_Cmd+CIMD_Cmdcnt;cmdp++)
1503 if (cmdp->code==CIMD_RX_Packet_slot[0].u.HexByte.i) break;
1504 if (cmdp>=CIMD_Cmd+CIMD_Cmdcnt)
1505 return; /* error - unknown command */
1506 CIMD_RX_Packet_Cmd=(enum CIMD_Cmd_Type)(cmdp-CIMD_Cmd);
1508 /* "position" handling is BIP dependency! */
1509 for (position=1;CIMD_RX_ProcessRawPacket_parseparam_buf_s<CIMD_CatchBufferPtr;position++) {
1510 struct CIMD_paramslot *slot;
1512 /* This code search is a crude overhead just for BIP now :-)
1514 for (param=cmdp->param;param<cmdp->param+cmdp->paramcnt;param++)
1515 if (position==param->code)
1517 if (param>=cmdp->param+cmdp->paramcnt) { /* not found - ignore */
1518 while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++); /* skip CIMD_MARK_SEP */
1521 slot=CIMD_RX_Packet_slot+1+(param-cmdp->param);
1522 slot->code=param->code;
1523 slot->elf=param->elf;
1524 if (!CIMD_RX_ProcessRawPacket_parseparam(slot))
1525 return; /* error - unable to parse required param */
1526 /* CIMD_MARK_SEP is skipped by CIMD_RX_ProcessRawPacket_parseparam() automatically
1528 CIMD_RX_Packet_slots++;
1530 /* The packet is now parsed */
1532 CIMD_RX_ProcessPacket();
1535 /* RX_State machine for receive handling. Called once for each character
1536 received from the phone/phone. */
1538 static void CIMD_RX_Char(char rx_byte)
1542 dprintf(_("Received character '%c' (0x%02X)\n"),
1543 (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
1548 /* NEVER store '\0' as it would knock-out completely our patrolling system!
1549 * It is invalid anyway
1551 if (rx_byte=='\0') {
1552 CIMD_CatchBufferReset();
1556 if (CIMD_CatchBufferPtr==CIMD_CatchBuffer && rx_byte!=CIMD_MARK_START) {
1557 /* No haven't yet catched any START yet */
1560 if (CIMD_CatchBufferPtr>=CIMD_CatchBuffer+sizeof(CIMD_CatchBuffer)) {
1561 fprintf(stderr,_("WARNING: Incoming CIMD packet too long to fit (>%d bytes)!\n"),sizeof(CIMD_CatchBuffer));
1562 CIMD_CatchBufferReset();
1565 *CIMD_CatchBufferPtr++=rx_byte;
1566 if (rx_byte==CIMD_MARK_STOP) {
1567 CIMD_RX_ProcessRawPacket();
1568 CIMD_CatchBufferReset();
1572 /* Here we initialise model specific functions. */
1574 #define CIMD_FUNCTIONS_ENTRY(name) \
1575 case GOP_##name: return(CIMD_##name(data,state));
1577 static GSM_Error CIMD_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
1579 static GSM_Statemachine *catcher=NULL;
1581 if (!catcher || catcher==state)
1587 CIMD_FUNCTIONS_ENTRY(Init)
1588 CIMD_FUNCTIONS_ENTRY(Terminate)
1589 CIMD_FUNCTIONS_ENTRY(GetSMSStatus)
1590 CIMD_FUNCTIONS_ENTRY(GetSMS)
1591 CIMD_FUNCTIONS_ENTRY(SendSMS)
1592 CIMD_FUNCTIONS_ENTRY(GetImei)
1593 CIMD_FUNCTIONS_ENTRY(GetRevision)
1594 CIMD_FUNCTIONS_ENTRY(GetModel)
1595 CIMD_FUNCTIONS_ENTRY(GetManufacturer)
1596 CIMD_FUNCTIONS_ENTRY(Reset)
1598 case GOP_Identify: {
1599 GSM_Error err,r=GE_NONE;
1600 if (GE_NONE!=(err=CIMD_GetImei (data,state)))
1602 if (GE_NONE!=(err=CIMD_GetRevision (data,state)))
1604 if (GE_NONE!=(err=CIMD_GetModel (data,state)))
1606 if (GE_NONE!=(err=CIMD_GetManufacturer(data,state)))
1612 return(GE_NOTIMPLEMENTED);
1616 GSM_Phone phone_cimd = {
1617 UNIMPLEMENTED, /* IncomingFunctions - we don't use default StateMachine */
1618 UNIMPLEMENTED, /* DefaultFunction - we don't use default StateMachine */
1619 /* Mobile phone information */
1621 "BIP" /* |CIMD - not yet */, /* Supported models */
1622 100, /* Max RF Level (AT+CSQ) */
1623 0, /* Min RF Level (AT+CSQ) */
1624 GRF_Percentage, /* RF level units */
1625 100, /* Max Battery Level (AT+CBC) */
1626 0, /* Min Battery Level (AT+CBC) */
1627 GBU_Percentage, /* Battery level units */
1628 GDT_None, /* Have date/time support */
1629 GDT_None, /* Alarm supports time only */
1630 0, /* No alarm available */
1631 48, 84, /* Startup logo size */
1632 14, 72, /* Op logo size */
1633 14, 72, /* Caller logo size */