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.2 2002/04/03 01:44:15 short
21 Implemented connection type "tcp" (GCT_TCP), use <hostname>:<port> as "port"
23 Revision 1.1.1.1 2002/04/03 00:08:03 short
24 Found in "gnokii-working" directory, some November-patches version
31 /* System header files */
43 #include "win32/winserial.h"
48 #define WRITEPHONE(a, b, c) WriteCommBlock(b, c)
49 #define sleep(x) Sleep((x) * 1000)
50 #define usleep(x) Sleep(((x) < 1000) ? 1 : ((x) / 1000))
55 #define WRITEPHONE(a, b, c) device_write(b, c)
61 #include <sys/ioctl.h>
62 #include <sys/types.h>
67 #include "devices/unixserial.h"
71 /* Various header file */
75 #include "gsm-common.h"
76 #include "cfgreader.h"
78 /* Global variables used by code in gsm-api.c to expose the functions
79 supported by this model of phone. */
83 /* fd opened in device.c */
84 extern int device_portfd;
87 /* Our private defines */
89 /* Define if plain (non-Bin) Submit is needed for 7bit messages
91 #define CIMD_SUBMIT_7BIT 1
93 /* When now catchbuffer was provided and must have some space to decode
94 * OK/ERROR/... result codes.
96 #define CIMD_CATCHBUFFER_LENGTH 0x400
98 /* +1 of maximum position number of CIMD protocol parameter */
99 #define CIMD_PARAMSLOTS (0x10)
101 /* We assume the 'right one' class is 1 (Mobile Equipment specific) */
102 #define DEFAULT_CLASS 1
104 /* CIMD_Param_Nak_Error codes:
106 #define CIMD_NAK_NO_SMS (0x5001)
107 #define CIMD_NAK_KEEPALIVE_REPLY (0x9998)
108 #define CIMD_NAK_RESEND (0x9999)
110 /* Local variables */
113 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH];
115 static bool RequestTerminate;
118 static u8 CIMD_CatchBuffer[CIMD_CATCHBUFFER_LENGTH];
119 static u8 *CIMD_CatchBufferPtr=CIMD_CatchBuffer; /* current destination writing ptr */
120 static GSM_Error *CIMD_CatchBufferErrorP;
122 static void CIMD_CatchBufferStart(GSM_Error *errorcodep);
125 #define CIMD_MARK_START (0x02)
126 #define CIMD_MARK_STOP (0x03)
127 #define CIMD_MARK_SEP (0x09)
128 #define CIMD_MARK_SEPS "\x09"
130 /* ELF=ELement Format */
145 enum CIMD_Param_SAMPLE { /* we need some universal enum for typecasting all params */
146 CIMD_Param_SAMPLE_dummy,
151 enum CIMD_Param_Ack {
153 CIMD_Param_Ack_Error,
156 static const struct CIMD_Param CIMD_Param_BIP_Ack[]={
157 { /* CIMD_Param_Ack_Cmd */ 1,CIMD_ELF_HexByte },
158 { /* CIMD_Param_Ack_Error */ 2,CIMD_ELF_StringZ }, /* protocol version on Cmd_Login, otherwise 0x0000 */
163 enum CIMD_Param_Nak {
165 CIMD_Param_Nak_Error,
168 static const struct CIMD_Param CIMD_Param_BIP_Nak[]={
169 { /* CIMD_Param_Nak_Cmd */ 1,CIMD_ELF_HexByte },
170 { /* CIMD_Param_Nak_Error */ 2,CIMD_ELF_HexWord },
175 enum CIMD_Param_Login {
177 CIMD_Param_Login_PWD,
178 CIMD_Param_Login_NULL
180 static const struct CIMD_Param CIMD_Param_BIP_Login[]={
181 { /* CIMD_Param_Login_ID */ 1,CIMD_ELF_StringZ },
182 { /* CIMD_Param_Login_PWD */ 2,CIMD_ELF_StringZ },
187 enum CIMD_Param_Logout {
188 CIMD_Param_Logout_NULL
190 static const struct CIMD_Param CIMD_Param_BIP_Logout[]={
193 /* CIMD_Cmd_Retrieve:
195 enum CIMD_Param_Retrieve {
196 CIMD_Param_Retrieve_NULL
198 static const struct CIMD_Param CIMD_Param_BIP_Retrieve[]={
201 /* CIMD_Cmd_RetrieveReply:
203 enum CIMD_Param_RetrieveReply {
204 CIMD_Param_RetrieveReply_Destination,
205 CIMD_Param_RetrieveReply_SourceApplication, /* ??? */
206 CIMD_Param_RetrieveReply_Text,
207 CIMD_Param_RetrieveReply_Timestamp,
208 CIMD_Param_RetrieveReply_Is8bit, /* protocol version 1.14+, CIMD_Param_DCSEnable_Type_*DCS* reqd */
209 CIMD_Param_RetrieveReply_PID_DCS, /* protocol version 1.15+, CIMD_Param_DCSEnable_Type_*PID_DCS* reqd */
210 CIMD_Param_RetrieveReply_SPEC, /* protocol version 1.16+, CIMD_Param_DCSEnable_Type_*SPEC* reqd */
211 CIMD_Param_RetrieveReply_NULL
213 static const struct CIMD_Param CIMD_Param_BIP_RetrieveReply[]={
214 { /* CIMD_Param_RetrieveReply_Destination */ 1,CIMD_ELF_StringZ },
215 { /* CIMD_Param_RetrieveReply_SourceApplication */ 2,CIMD_ELF_StringZ },
216 { /* CIMD_Param_RetrieveReply_Text */ 3,CIMD_ELF_StringZ },
217 { /* CIMD_Param_RetrieveReply_Timestamp */ 4,CIMD_ELF_StringZ },
218 { /* CIMD_Param_RetrieveReply_Is8bit */ 5,CIMD_ELF_Decimal },
219 { /* CIMD_Param_RetrieveReply_PID_DCS */ 6,CIMD_ELF_Decimal }, /* upper=PID, lower=DCS, FIXME: should be HexWord! */
220 { /* CIMD_Param_RetrieveReply_SPEC */ 7,CIMD_ELF_Decimal }, /* FIXME: should be HexByte! */
225 enum CIMD_Param_Count {
226 CIMD_Param_Count_NULL
228 static const struct CIMD_Param CIMD_Param_BIP_Count[]={
231 /* CIMD_Cmd_CountReply:
233 enum CIMD_Param_CountReply {
234 CIMD_Param_CountReply_Used, /* Used==NotRead==Slots */
235 CIMD_Param_CountReply_NULL
237 static const struct CIMD_Param CIMD_Param_BIP_CountReply[]={
238 { /* CIMD_Param_CountReply_Used */ 1,CIMD_ELF_Decimal },
243 enum CIMD_Param_Submit {
244 CIMD_Param_Submit_Destination,
245 CIMD_Param_Submit_Text,
246 CIMD_Param_Submit_ValidityPeriod,
247 CIMD_Param_Submit_AUX, /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
248 CIMD_Param_Submit_DCS, /* protocol version 1.14+ */
249 CIMD_Param_Submit_PID, /* protocol version 1.15+ */
250 CIMD_Param_Submit_SPEC, /* protocol version 1.16+ */
251 CIMD_Param_Submit_NULL
253 static const struct CIMD_Param CIMD_Param_BIP_Submit[]={
254 { /* CIMD_Param_Submit_Destination */ 1,CIMD_ELF_StringZ },
255 { /* CIMD_Param_Submit_Text */ 2,CIMD_ELF_StringZ },
256 { /* CIMD_Param_Submit_ValidityPeriod */ 3,CIMD_ELF_HexByte },
257 { /* CIMD_Param_Submit_AUX */ 4,CIMD_ELF_Empty },
258 { /* CIMD_Param_Submit_DCS */ 5,CIMD_ELF_Decimal },
259 { /* CIMD_Param_Submit_PID */ 6,CIMD_ELF_HexByte },
260 { /* CIMD_Param_Submit_SPEC */ 7,CIMD_ELF_HexByte },
263 /* CIMD_Cmd_SubmitBin: protocol version 1.12+
265 enum CIMD_Param_SubmitBin {
266 CIMD_Param_SubmitBin_Destination,
267 CIMD_Param_SubmitBin_Text,
268 CIMD_Param_SubmitBin_ValidityPeriod,
269 CIMD_Param_SubmitBin_AUX, /* protocol version 1.13+, empty for BMG<->SMSC link CIMD (just for OIS) */
270 CIMD_Param_SubmitBin_DCS, /* protocol version 1.14+ */
271 CIMD_Param_SubmitBin_PID, /* protocol version 1.15+ */
272 CIMD_Param_SubmitBin_SPEC, /* protocol version 1.16+ */
273 CIMD_Param_SubmitBin_NULL
275 static const struct CIMD_Param CIMD_Param_BIP_SubmitBin[]={
276 { /* CIMD_Param_SubmitBin_Destination */ 1,CIMD_ELF_StringZ },
277 { /* CIMD_Param_SubmitBin_Text */ 2,CIMD_ELF_StringZ },
278 { /* CIMD_Param_SubmitBin_ValidityPeriod */ 3,CIMD_ELF_HexByte },
279 { /* CIMD_Param_SubmitBin_AUX */ 4,CIMD_ELF_Empty },
280 { /* CIMD_Param_SubmitBin_DCS */ 5,CIMD_ELF_Decimal },
281 { /* CIMD_Param_SubmitBin_PID */ 6,CIMD_ELF_HexByte },
282 { /* CIMD_Param_SubmitBin_SPEC */ 7,CIMD_ELF_HexByte },
285 /* CIMD_Cmd_DCSEnable: protocol version 1.14+
287 enum CIMD_Param_DCSEnable {
288 CIMD_Param_DCSEnable_Type,
289 CIMD_Param_DCSEnable_NULL
291 static const struct CIMD_Param CIMD_Param_BIP_DCSEnable[]={
292 { /* CIMD_Param_DCSEnable_Type */ 1,CIMD_ELF_HexWord },
295 enum CIMD_Param_DCSEnable_Type {
296 CIMD_Param_DCSEnable_Type_None =0x0000, /* protocol version 1.14+ */
297 CIMD_Param_DCSEnable_Type_DCS =0x0001, /* protocol version 1.14+ */
298 CIMD_Param_DCSEnable_Type_PID_DCS =0x0002, /* protocol version 1.15+ */
299 CIMD_Param_DCSEnable_Type_PID_DCS_SPEC=0x0003, /* protocol version 1.16+ */
302 #define CIMD_PARAM_ENTRY(param) (param),ARRAY_LEN((param))
306 const struct CIMD_Param *param;
317 CIMD_Cmd_RetrieveReply,
320 CIMD_Cmd_SubmitBin, /* protocol version 1.12+ */
321 CIMD_Cmd_DCSEnable, /* protocol version 1.14+ */
322 CIMD_Cmd_NULL /* stdarg termination, MUST be last! */
324 static const struct CIMD_Cmd CIMD_Cmd_BIP[]={
325 { /* CIMD_Cmd_Ack */ 0x00,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Ack ) },
326 { /* CIMD_Cmd_Nak */ 0x99,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Nak ) },
327 { /* CIMD_Cmd_Login */ 0x01,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Login ) },
328 { /* CIMD_Cmd_Logout */ 0x02,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Logout ) },
329 { /* CIMD_Cmd_Submit */ 0x03,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Submit ) },
330 { /* CIMD_Cmd_Retrieve */ 0x05,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Retrieve ) },
331 { /* CIMD_Cmd_RetrieveReply */ 0x06,CIMD_PARAM_ENTRY(CIMD_Param_BIP_RetrieveReply) },
332 { /* CIMD_Cmd_Count */ 0x51,CIMD_PARAM_ENTRY(CIMD_Param_BIP_Count ) },
333 { /* CIMD_Cmd_CountReply */ 0x61,CIMD_PARAM_ENTRY(CIMD_Param_BIP_CountReply ) },
334 { /* CIMD_Cmd_SubmitBin */ 0x31,CIMD_PARAM_ENTRY(CIMD_Param_BIP_SubmitBin ) },
335 { /* CIMD_Cmd_DCSEnable */ 0x53,CIMD_PARAM_ENTRY(CIMD_Param_BIP_DCSEnable ) },
338 static const struct CIMD_Cmd *CIMD_Cmd=CIMD_Cmd_BIP; /* FIXME: When other protocols get supported... */
339 static unsigned CIMD_Cmdcnt=ARRAY_LEN(CIMD_Cmd_BIP); /* FIXME: When other protocols get supported... */
341 struct CIMD_Param_Ack_Nak_Error {
346 static const struct CIMD_Param_Ack_Nak_Error CIMD_Param_Ack_Nak_Error[]={
348 { 0x1101,N_("User is already logged in") },
349 { 0x1001,N_("Logging in is currently disabled") },
350 { 0x9999,N_("Username not found") },
351 /* 0x000? can be bitmask combined from: */
352 { 0x0001,N_("Invalid password") },
353 { 0x0002,N_("User has forbidden access") },
354 { 0x0008,N_("User is already registered") }, /* ??? difference from 0x1101 ? */
355 /* CIMD_Cmd_Retrieve / CIMD_Cmd_Count */
356 { 0x5001,N_("No SMS found or not detectable") }, /* ==CIMD_NAK_NO_SMS */
357 { 0x5011,N_("Function not enabled") },
358 /* CIMD_Cmd_Retrieve */
359 { 0x5021,N_("Automatical retrieve is active") },
360 /* CIMD_Cmd_Submit / CIMD_Cmd_SubmitBin */
361 { 0x3001,N_("Error during processing SMS in BMG") },
362 { 0x3061,N_("Destination number has invalid format") },
363 /* CIMD_Cmd_SubmitBin */
364 { 0x3051,N_("Invalid DCS") }, /* protocol version 1.14+ */
365 { 0x3081,N_("Invalid Text") }, /* protocol version 1.14+ */
366 { 0x3091,N_("SMS is empty") }, /* protocol version 1.14+ */
367 { 0x3099,N_("SMS submit too fast") }, /* protocol version 1.16+ */
371 static struct CIMD_paramslot CIMD_RX_Packet_slot[CIMD_PARAMSLOTS];
372 #define CIMD_RX_PACKET_PARAMSLOT(paramsample) (CIMD_RX_Packet_slot+1+(unsigned)(paramsample))
373 static enum CIMD_Cmd_Type CIMD_RX_Packet_Cmd;
374 static unsigned CIMD_RX_Packet_slots;
377 /* RETURNS: Processed */
378 typedef bool (*CIMD_RX_PatrolFunc)(void);
379 typedef void (*CIMD_RX_PatrolReset)(void);
381 struct CIMD_RX_Patrol {
382 enum CIMD_Cmd_Type cmd;
383 CIMD_RX_PatrolFunc func;
384 CIMD_RX_PatrolReset reset;
387 static const struct CIMD_RX_Patrol *CIMD_RX_Patrol_Current;
389 static char *CIMD_RX_Patrol_Ack_Error;
390 static u16 CIMD_RX_Patrol_Nak_Error;
395 static pthread_t Thread;
397 static pthread_t selThread;
402 /* Local variables used by get/set phonebook entry code. Buffer is used as a
403 source or destination for phonebook data and other functions... Error is
404 set to GE_NONE by calling function, set to GE_COMPLETE or an error code by
405 handler routines as appropriate. */
407 static GSM_SMSMessage *CurrentSMSMessage;
408 static GSM_Error CurrentSMSMessageError;
410 static GSM_SMSStatus *CurrentSMSStatus;
411 static GSM_Error CurrentSMSStatusError;
413 static unsigned char Revision[GSM_MAX_REVISION_LENGTH];
414 static unsigned char Model [GSM_MAX_MODEL_LENGTH];
417 static u8 CIMD_TX_SendCommand_buf[sizeof(CIMD_CatchBuffer)]; /* sizeof() is not required to be == but it is appropriate */
418 static u8 *CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
420 struct CIMD_paramslot {
424 struct { u8 i; } HexByte;
425 struct { u16 i; } HexWord;
426 struct { int i; } Decimal;
427 struct { char *s; } StringZ;
428 struct { u8 *buf; size_t len; } HexBlock;
429 struct { int _dummy; } Empty;
433 /* slots are sorted by moving the whole blocks but they are small so it is OK
435 static struct CIMD_paramslot CIMD_TX_SendCommand_paramslots[CIMD_PARAMSLOTS];
436 static int CIMD_TX_SendCommand_paramslots_full;
437 static enum CIMD_Cmd_Type CIMD_TX_SendCommand_Cmd;
441 static bool CIMD_TX_SendCommand_vpushparam(u16 code,enum CIMD_ELF elf,va_list *app)
443 struct CIMD_paramslot *slot=CIMD_TX_SendCommand_paramslots+(CIMD_TX_SendCommand_paramslots_full++);
445 if (slot>=CIMD_TX_SendCommand_paramslots+ARRAY_LEN(CIMD_TX_SendCommand_paramslots)) {
447 fprintf(stderr,"Out of param slots!\n");
453 case CIMD_ELF_HexByte:
454 slot->u.HexByte.i=va_arg((*app),int);
456 case CIMD_ELF_HexWord:
457 slot->u.HexWord.i=va_arg((*app),int);
459 case CIMD_ELF_Decimal:
460 slot->u.Decimal.i=va_arg((*app),int);
462 case CIMD_ELF_StringZ:
463 slot->u.StringZ.s=va_arg((*app),char *);
465 case CIMD_ELF_HexBlock:
466 slot->u.HexBlock.buf=va_arg((*app),u8 *);
467 slot->u.HexBlock.len=va_arg((*app),size_t);
477 static bool CIMD_TX_SendCommand_pushparam(u16 code,enum CIMD_ELF elf,...)
483 r=CIMD_TX_SendCommand_vpushparam(code,elf,&ap);
488 static int CIMD_TX_SendCommand_paramslots_compare
489 (const struct CIMD_paramslot *a,const struct CIMD_paramslot *b)
491 return((b->code<a->code)-(a->code<b->code));
496 static bool CIMD_TX_SendCommand_storeparam(struct CIMD_paramslot *slot)
499 case CIMD_ELF_HexByte:
500 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",slot->u.HexByte.i);
502 case CIMD_ELF_HexWord:
503 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%04X",slot->u.HexWord.i);
505 case CIMD_ELF_Decimal:
506 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%d",slot->u.Decimal.i);
508 case CIMD_ELF_StringZ: {
510 if (!slot->u.StringZ.s) /* NULL is interpreted as "" */
512 len=strlen(slot->u.StringZ.s);
513 if (CIMD_TX_SendCommand_buf_d+len > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
514 return(false); /* error - overflow */
515 memcpy(CIMD_TX_SendCommand_buf_d,slot->u.StringZ.s,len);
516 CIMD_TX_SendCommand_buf_d+=len;
518 case CIMD_ELF_HexBlock:
519 if (CIMD_TX_SendCommand_buf_d+2*slot->u.HexBlock.len
520 > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf))
521 return(false); /* error - overflow */
522 CIMD_TX_SendCommand_buf_d=SMS_BlockToHex(CIMD_TX_SendCommand_buf_d,
523 slot->u.HexBlock.buf,slot->u.HexBlock.len); /* never fails */
528 return(true); /* success */
531 static unsigned CIMD_KeepAlives=0;
532 static pthread_mutex_t CIMD_KeepAlivesLock;
534 /* This function is NOT thread-safe!
536 static void CIMD_KeepAlivesLockInit(void)
538 static bool done=false;
542 pthread_mutex_init(&CIMD_KeepAlivesLock,NULL);
546 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...);
548 static void CIMD_KeepAlivesCheck(void)
552 CIMD_KeepAlivesLockInit();
553 pthread_mutex_lock(&CIMD_KeepAlivesLock);
554 alives=CIMD_KeepAlives;
556 pthread_mutex_unlock(&CIMD_KeepAlivesLock);
559 CIMD_TX_SendCommand(NULL/*errorcodep*/,NULL/*patrol*/,
561 CIMD_Param_Nak_Cmd,(CIMD_Cmd+(unsigned)CIMD_Cmd_Ack)->code, /* ==0x00 */
562 CIMD_Param_Nak_Error,CIMD_NAK_KEEPALIVE_REPLY, /* ==0x9998 */
563 CIMD_Param_Nak_NULL);
570 static bool CIMD_TX_SendCommandResend(boid)
574 writephone_got=WRITEPHONE(PortFD, CIMD_TX_SendCommand_buf, CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf);
577 write(1,"CMD:<STX>",9);
578 write(1,CIMD_TX_SendCommand_buf+1,CIMD_TX_SendCommand_buf_d-1-(CIMD_TX_SendCommand_buf+1));
579 write(1,"<ETX>\n",6);
582 return(writephone_got==(CIMD_TX_SendCommand_buf_d-CIMD_TX_SendCommand_buf));
585 static void CIMD_TX_SendCommand(GSM_Error *errorcodep,const struct CIMD_RX_Patrol *patrol,enum CIMD_Cmd_Type cmd,...)
588 const struct CIMD_Cmd *cmdstruct;
589 enum CIMD_Param_SAMPLE param;
590 const struct CIMD_Param *paramstruct;
592 struct CIMD_paramslot *slot;
595 CIMD_KeepAlivesCheck();
600 CIMD_RX_Patrol_Current=patrol;
602 CIMD_TX_SendCommand_Cmd=cmd;
603 CIMD_TX_SendCommand_paramslots_full=0;
604 if ((unsigned)cmd>=CIMD_Cmdcnt)
605 goto fail; /* assert */
606 cmdstruct=CIMD_Cmd+(unsigned)cmd;
608 if (!CIMD_TX_SendCommand_pushparam(0/*code*/,CIMD_ELF_HexByte/*elf*/,cmdstruct->code))
611 param=va_arg(ap,enum CIMD_Param_SAMPLE);
612 if ((unsigned)param==cmdstruct->paramcnt)
613 break; /* CIMD_Param_*_NULL reached */
614 if ((unsigned)param>cmdstruct->paramcnt)
615 goto fail; /* assert */
616 paramstruct=(cmdstruct->param+(unsigned)param);
617 if (!CIMD_TX_SendCommand_vpushparam(paramstruct->code,paramstruct->elf,&ap))
620 qsort(CIMD_TX_SendCommand_paramslots,CIMD_TX_SendCommand_paramslots_full,
621 sizeof(*CIMD_TX_SendCommand_paramslots),
622 (int (*)(const void *,const void *))CIMD_TX_SendCommand_paramslots_compare);
624 CIMD_TX_SendCommand_buf_d=CIMD_TX_SendCommand_buf;
625 *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_START;
627 slot=CIMD_TX_SendCommand_paramslots;
628 for (parami=0;slot<CIMD_TX_SendCommand_paramslots+CIMD_TX_SendCommand_paramslots_full;parami++) {
629 if (CIMD_TX_SendCommand_buf_d +32/*safety*/ > CIMD_TX_SendCommand_buf_d+sizeof(CIMD_TX_SendCommand_buf)) {
630 /* error - overflow */
633 *errorcodep=GE_INTERNALERROR;
636 if (slot->code==parami) {
637 if (!CIMD_TX_SendCommand_storeparam(slot)) /* error */
640 if (slot->code>=parami)
641 *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_SEP;
642 if (slot->code<=parami)
646 for (u8s=CIMD_TX_SendCommand_buf;u8s<CIMD_TX_SendCommand_buf_d;u8s++)
648 CIMD_TX_SendCommand_buf_d+=sprintf(CIMD_TX_SendCommand_buf_d,"%02X",(unsigned)xsum);
649 *CIMD_TX_SendCommand_buf_d++=CIMD_MARK_STOP;
651 CIMD_CatchBufferStart(errorcodep);
653 if (!CIMD_TX_SendCommandResend())
656 /* success but we don't wait for the result code */
659 /* This function is used to get storage status from the phone. It currently
660 supports two different memory areas - internal and SIM. */
663 wait_on(volatile GSM_Error *what, int timeout)
665 GSM_Error r=GE_TIMEOUT; /* shut up GCC when (timeout==0) */
667 while (timeout && ((r=*what)==GE_BUSY)) {
672 CIMD_KeepAlivesCheck();
675 /* any specific patrollers are no longer valid */
676 CIMD_RX_Patrol_Current=NULL;
679 printf("wait_on finish, timeout=%d\n",(r==GE_TIMEOUT));
685 #define WAIT_ON(what, timeout) \
687 GSM_Error res = wait_on(what, timeout); \
688 if (res != GE_NONE) \
692 /* I hope GCC gets this bunch optimized on the normal the normal errorcodep!=NULL case
694 #define CIMD_TX_SENDCOMMAND_WAIT_ON(errorcodep,timeout,patrol,args...) \
696 GSM_Error _CIMD_TX_SENDCOMMAND_WAIT_ON_err,*_CIMD_TX_SENDCOMMAND_WAIT_ON_errp; \
698 if (!(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp=(errorcodep))) \
699 _CIMD_TX_SENDCOMMAND_WAIT_ON_errp=&_CIMD_TX_SENDCOMMAND_WAIT_ON_err; \
700 CIMD_TX_SendCommand(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(patrol),args); \
701 WAIT_ON(_CIMD_TX_SENDCOMMAND_WAIT_ON_errp,(timeout)); \
705 #define CIMD_ERR_WRAPPER(expr) \
707 GSM_Error err=(expr); \
712 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
714 static unsigned char CIMD_SMStoSPEC_BIP_CIMD(GSM_SMSMessage *SMS)
717 /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
719 |((!!SMS->ReplyViaSameSMSC)<<1) /*ReplyPath*/
720 |((!!SMS->UDHPresent)<<2) /*UDH set*/
724 static unsigned char CIMD_SMStoDCS(GSM_SMSMessage *SMS)
726 int class=(SMS->Class==-1 ? DEFAULT_CLASS : SMS->Class);
728 #if 0 /* NEVER send 0 for BIP CIMD as it would translate it to F6 !!!
730 if (!SMS->EightBit && class==DEFAULT_CLASS)
733 return(0xF0 | ((!!SMS->EightBit)<<2) | ((class&0x03)<<0));
736 static GSM_Error CIMD_PhoneSetup(void)
739 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,100/*timeout*/,NULL/*patrol*/,
741 /* NULLs will be interpreted as ""
743 CIMD_Param_Login_ID,CFG_Get(CFG_Info,"CIMD","name"),
744 CIMD_Param_Login_PWD,CFG_Get(CFG_Info,"CIMD","password"),
745 CIMD_Param_Login_NULL);
746 if (!CIMD_RX_Patrol_Ack_Error)
749 SAFE_STRNCPY_SIZEOF(Revision,CIMD_RX_Patrol_Ack_Error);
751 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,10/*timeout*/,NULL/*patrol*/,
753 CIMD_Param_DCSEnable_Type,(u16)CIMD_Param_DCSEnable_Type_PID_DCS_SPEC,
754 CIMD_Param_DCSEnable_NULL);
761 static void CIMD_RX_Char(char rx_byte);
762 static void CIMD_SigHandler(int status);
763 static bool CIMD_OpenSerial(GSM_ConnectionType connection);
765 GSM_Phone phone_cimd; /* forward declaration */
767 /* Initialise variables and state machine. */
769 static GSM_Error CIMD_Init(GSM_Data *data, GSM_Statemachine *state)
771 RequestTerminate = false;
773 SAFE_STRNCPY_SIZEOF(Model,data->Model);
775 /* Create and start main thread. */
780 rtn = ! OpenConnection(State->Link.PortDevice,CIMD_RX_Char,CIMD_KeepAliveProc);
782 return(GE_INTERNALERROR);
785 SAFE_STRNCPY_SIZEOF(PortDevice,state->Link.PortDevice);
786 if (!CIMD_OpenSerial(state->Link.ConnectionType))
787 return(GE_INTERNALERROR);
790 CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
796 /* thread for handling incoming data */
797 void CIMD_SelectLoop()
801 struct timeval timeout;
804 FD_SET(device_portfd, &readfds);
805 /* set timeout to 15 seconds */
808 while (!RequestTerminate) {
809 err = select(device_portfd + 1, &readfds, NULL, NULL, &timeout);
810 /* call singal handler to process incoming data */
811 if ( err > 0 ) CIMD_SigHandler(0);
812 else if (err == -1) perror("Error in SelectLoop");
817 /* Applications should call CIMD_Terminate to shut down the CIMD thread and
818 close the serial port. */
820 static GSM_Error CIMD_Terminate(GSM_Data *data, GSM_Statemachine *state)
825 /* Don't wait too much as we can have already broken link
827 CIMD_TX_SendCommand(&err/*errorcodep*/,NULL/*patrol*/,
829 CIMD_Param_Logout_NULL);
830 wait_on(&err,20/*timeout*/); /* errors ignored, of course */
833 /* Request termination of thread */
834 RequestTerminate = true;
837 /* Now wait for thread to terminate. */
838 pthread_join(Thread, NULL);
840 /* Close serial port. */
850 /* messagecenter->No" is NOT set as it is input argument */
851 static void CIMD_MessageCenterClear(GSM_MessageCenter *messagecenter)
853 messagecenter->Name[0]='\0'; /* not present up to Nokia 9110i */
854 messagecenter->Recipient[0]='\0'; /* not present up to Nokia 9110i */
855 messagecenter->Number[0]='\0'; /* default */
856 messagecenter->Format=GSMF_Text; /* default */
857 messagecenter->Validity=GSMV_72_Hours; /* default */
860 static bool CIMD_RX_Patrol_CountReply(void)
862 CurrentSMSStatus->UnRead=/*FALLTHRU*/
863 CurrentSMSStatus->Used =/*FALLTHRU*/
864 CurrentSMSStatus->Slots =/*FALLTHRU*/
865 CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_CountReply_Used)->u.Decimal.i;
867 CurrentSMSStatusError=GE_NONE;
871 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_CountReply_struct=
872 { CIMD_Cmd_CountReply,CIMD_RX_Patrol_CountReply };
874 static GSM_Error CIMD_GetSMSStatus(GSM_Data *data, GSM_Statemachine *state)
876 CurrentSMSStatus = data->SMSStatus;
877 CurrentSMSStatus->UnRead=0; /* default */
878 CurrentSMSStatus->Used =0; /* default */
879 CurrentSMSStatus->Slots =0; /* default */
881 CIMD_TX_SENDCOMMAND_WAIT_ON(&CurrentSMSStatusError/*errorcodep*/,100/*timeout*/,&CIMD_RX_Patrol_CountReply_struct/*patrol*/,
883 CIMD_Param_Count_NULL);
889 static GSM_Error CIMD_GetImei(GSM_Data *data, GSM_Statemachine *state)
891 /* not supported by the protocol */
896 static GSM_Error CIMD_GetRevision(GSM_Data *data, GSM_Statemachine *state)
899 strcpy(data->Revision,Revision);
901 } else return (GE_TRYAGAIN);
904 static GSM_Error CIMD_GetModel(GSM_Data *data, GSM_Statemachine *state)
906 /* not supported by the protocol */
907 strcpy(data->Model,Model);
911 static GSM_Error CIMD_GetManufacturer(GSM_Data *data, GSM_Statemachine *state)
913 /* not supported by the protocol */
918 static void CIMD_DateTimeSetCurrent(GSM_DateTime *datetime)
920 time_t current=time(NULL);
921 struct tm *tm=localtime(¤t);
923 datetime->AlarmEnabled=false;
925 datetime->Year =tm->tm_year+1900;
926 datetime->Month =tm->tm_mon+1;
927 datetime->Day =tm->tm_mday;
928 datetime->Hour =tm->tm_hour;
929 datetime->Minute=tm->tm_min;
930 datetime->Second=tm->tm_sec;
932 datetime->Timezone=timezone;
935 /* scts=="18.06.1998 22:33:23"
937 static bool CIMD_SCTStoSMS(GSM_SMSMessage *SMS,const char *scts)
939 GSM_DateTime *DateTime=&SMS->Time;
940 const char *fmt="%2d.%2d.%4d %2d:%2d:%2d"; /* trailing '\0' IS used! */
947 int nums=(*++fs)-'0';
955 /* string is completely valid now */
964 DateTime->Timezone=0;
969 static void CIMD_DCStoSMS(GSM_SMSMessage *SMS,unsigned char dcs)
971 switch ((dcs&0xF0)>>4) {
975 CurrentSMSMessage->EightBit=false;
980 CurrentSMSMessage->EightBit=!!(dcs&0x04); /* bit 2 */
981 CurrentSMSMessage->Class=(dcs&0x03); /* bits 0 & 1 */
986 /* Convert SPEC field of BIP protocol using CIMD protocol on BMG<->SMSC link
988 static void CIMD_SPEC_BIP_CIMDtoSMS(GSM_SMSMessage *SMS,u8 spec)
990 /* Such specification not supported by BIP:
992 CurrentSMSMessage->Type=GST_MT;
993 CurrentSMSMessage->Status=GSS_NOTSENTREAD;
995 /* bit 0 (enable) & bits 4-7 (value) - priority - not supported by Gnokii
998 SMS->UDHPresent=!!(spec&(1<<2));
1000 SMS->ReplyViaSameSMSC=!!(spec&(1<<1));
1003 static bool CIMD_RX_Patrol_RetrieveReply(void)
1005 u16 pid_dcs; /* upper=PID, lower=DCS */
1008 const char *destination=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Destination)->u.StringZ.s;
1010 if (strlen(destination)+1 > sizeof(CurrentSMSMessage->Sender)) {
1012 CurrentSMSMessageError=GE_INTERNALERROR;
1013 return(true); /* error */
1015 strcpy(CurrentSMSMessage->Sender,destination);
1018 if ((CurrentSMSMessage->EightBit=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Is8bit)->u.Decimal.i)) {
1019 const char *hextext=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1020 size_t hextextlen=strlen(hextext);
1022 if ((hextextlen&1) || (hextextlen/2 > sizeof(CurrentSMSMessage->MessageText)))
1023 goto fail; /* error - message too long */
1024 if (!SMS_BlockFromHex(CurrentSMSMessage->MessageText/*d*/,hextext,hextextlen))
1025 goto fail; /* error - parse error */
1026 CurrentSMSMessage->MessageTextLength=hextextlen/2;
1029 const char *text=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Text)->u.StringZ.s;
1030 size_t textlen=strlen(text);
1032 if (textlen+1 > sizeof(CurrentSMSMessage->MessageText))
1033 goto fail; /* error - message too long */
1034 strcpy(CurrentSMSMessage->MessageText,text);
1035 CurrentSMSMessage->MessageTextLength=textlen;
1038 /* errors ignored as it is not fatal */
1039 CIMD_SCTStoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_Timestamp)->u.StringZ.s);
1041 pid_dcs=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_PID_DCS)->u.HexWord.i;
1043 /* FIXME: Is it correct to fill "MessageCenter" fields from SMS body? */
1044 CurrentSMSMessage->MessageCenter.Format=(GSM_SMSMessageFormat)(pid_dcs>>8U); /* <pid> */
1045 CIMD_DCStoSMS(CurrentSMSMessage,(pid_dcs&0x00FFU));
1046 CIMD_SPEC_BIP_CIMDtoSMS(CurrentSMSMessage,CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_RetrieveReply_SPEC)->u.HexByte.i);
1048 if (CurrentSMSMessage->UDHPresent) {
1050 if (CurrentSMSMessage->MessageTextLength<=0)
1052 udhlen=1/*sizeof(udhlen)*/ +CurrentSMSMessage->MessageText[0];
1053 if (udhlen > CurrentSMSMessage->MessageTextLength)
1055 memcpy(CurrentSMSMessage->UDH,CurrentSMSMessage->MessageText,udhlen);
1056 memmove(CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageText+udhlen,
1057 CurrentSMSMessage->MessageTextLength-udhlen +1/*Trailing '\0'*/);
1058 CurrentSMSMessage->MessageTextLength-=udhlen;
1061 CurrentSMSMessageError=GE_NONE;
1065 static const struct CIMD_RX_Patrol CIMD_RX_Patrol_RetrieveReply_struct=
1066 { CIMD_Cmd_RetrieveReply,CIMD_RX_Patrol_RetrieveReply };
1068 static GSM_Error CIMD_GetSMS(GSM_Data *data, GSM_Statemachine *state)
1070 CurrentSMSMessage = data->SMSMessage;
1072 if (CurrentSMSMessage->MemoryType!=GMT_SM)
1073 return(GE_INVALIDMEMORYTYPE);
1075 CIMD_DateTimeSetCurrent(&CurrentSMSMessage->Time); /* default */
1076 CIMD_DateTimeSetCurrent(&CurrentSMSMessage->SMSCTime); /* not present in the protocol */
1077 CurrentSMSMessage->MessageTextLength=0; /* default */
1078 CurrentSMSMessage->Validity=72/*hours*/*60; /* default */
1079 CurrentSMSMessage->UDHPresent=false; /* default */
1080 CurrentSMSMessage->MessageText[0]='\0'; /* default */
1082 CIMD_MessageCenterClear(&CurrentSMSMessage->MessageCenter); /* default */
1083 CurrentSMSMessage->MessageCenter.No=0; /* default - input for GetSMSCenter */
1085 CurrentSMSMessage->Sender[0]='\0'; /* default */
1086 CurrentSMSMessage->Destination[0]='\0'; /* default */
1087 CurrentSMSMessage->MessageNumber=CurrentSMSMessage->Location; /* default */
1088 /* CurrentSMSMessage->MemoryType is input argument */
1089 CurrentSMSMessage->Type=GST_UN; /* default, detection of EMPTY SMSes! */
1090 CurrentSMSMessage->Status=GSS_SENTREAD; /* default */
1091 CurrentSMSMessage->Class=DEFAULT_CLASS; /* default */
1092 CurrentSMSMessage->EightBit=false; /* default */
1093 CurrentSMSMessage->Compression=false; /* default */
1094 /* CurrentSMSMessage->Location is input argument */
1095 CurrentSMSMessage->ReplyViaSameSMSC=false; /* default */
1097 CIMD_TX_SendCommand(&CurrentSMSMessageError/*errorcodep*/,&CIMD_RX_Patrol_RetrieveReply_struct/*patrol*/,
1099 CIMD_Param_Retrieve_NULL);
1100 if (GE_NONE!=wait_on(&CurrentSMSMessageError,90/*timeout*/)) {
1101 if (CIMD_RX_Patrol_Nak_Error!=CIMD_NAK_NO_SMS)
1102 return(CurrentSMSMessageError);
1103 /* We don't return GE_EMPTYSMSLOCATION as when we have already eaten
1104 * all the wating SMSes there cannot be any other one
1106 return(GE_INVALIDSMSLOCATION);
1112 static GSM_Error CIMD_SendSMS(GSM_Data *data, GSM_Statemachine *state)
1114 u8 bintext[sizeof(CurrentSMSMessage->UDH)+sizeof(CurrentSMSMessage->MessageText)],*d;
1115 char hexbintext[sizeof(bintext) +1/*Trailing '\0'*/];
1117 CurrentSMSMessage = data->SMSMessage;
1119 CurrentSMSMessage->MessageNumber=0; /* default */
1122 if (CurrentSMSMessage->UDHPresent) {
1123 size_t UDHlen=1+CurrentSMSMessage->UDH[0];
1125 memcpy(d,CurrentSMSMessage->UDH,UDHlen);
1127 #ifdef CIMD_SUBMIT_7BIT
1128 if (!CurrentSMSMessage->EightBit) /* We are not able to send UDH by 7bit channel */
1129 return(GE_NOTSUPPORTED);
1133 #ifdef CIMD_SUBMIT_7BIT
1134 1 /* Never do 7bit->8bit encoding when CIMD_SUBMIT_7BIT */
1136 CurrentSMSMessage->EightBit
1139 memcpy(d,CurrentSMSMessage->MessageText,CurrentSMSMessage->MessageTextLength);
1140 d+=CurrentSMSMessage->MessageTextLength;
1142 #ifndef CIMD_SUBMIT_7BIT
1144 size_t byteslen=PackSevenBitsToEight(
1145 /* check it out yourself, really the number of used bits for UDH header on the start
1146 * as we will need to allocate initial bit to align SMS->MessageText on the 7-bit boundary
1149 CurrentSMSMessage->MessageText,d);
1155 #ifdef CIMD_SUBMIT_7BIT
1156 CurrentSMSMessage->EightBit
1158 1 /* Never use plain Submit for 8bit messages */
1161 *(SMS_BlockToHex(hexbintext,bintext,(d-bintext)/*len*/))='\0';
1163 /* DANGER: Keep in sync with CIMD_Cmd_Submit !!!
1165 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1167 CIMD_Param_SubmitBin_Destination,CurrentSMSMessage->Destination,
1168 CIMD_Param_SubmitBin_Text,hexbintext,
1169 CIMD_Param_SubmitBin_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1170 CIMD_Param_SubmitBin_AUX, /* Empty */
1171 CIMD_Param_SubmitBin_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1172 CIMD_Param_SubmitBin_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1173 CIMD_Param_SubmitBin_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1174 CIMD_Param_SubmitBin_NULL);
1176 #ifdef CIMD_SUBMIT_7BIT
1180 /* DANGER: Keep in sync with CIMD_Cmd_SubmitBin !!!
1182 CIMD_TX_SENDCOMMAND_WAIT_ON(NULL/*errorcodep*/,150/*timeout*/,NULL/*patrol*/,
1184 CIMD_Param_Submit_Destination,CurrentSMSMessage->Destination,
1185 CIMD_Param_Submit_Text,bintext,
1186 CIMD_Param_Submit_ValidityPeriod,SMS_Validity_to_VP(CurrentSMSMessage->Validity),
1187 CIMD_Param_Submit_AUX, /* Empty */
1188 CIMD_Param_Submit_DCS,(unsigned)CIMD_SMStoDCS(CurrentSMSMessage),
1189 CIMD_Param_Submit_PID,(u8)CurrentSMSMessage->MessageCenter.Format,
1190 CIMD_Param_Submit_SPEC,CIMD_SMStoSPEC_BIP_CIMD(CurrentSMSMessage),
1191 CIMD_Param_Submit_NULL);
1195 return(GE_SMSSENDOK);
1198 static GSM_Error CIMD_Reset(GSM_Data *data, GSM_Statemachine *state)
1200 CIMD_ERR_WRAPPER(CIMD_PhoneSetup());
1207 /* Called by initialisation code to open comm port in asynchronous mode. */
1209 static bool CIMD_OpenSerial(GSM_ConnectionType connection)
1216 struct sigaction sig_io;
1218 /* Set up and install handler before enabling async IO on port. */
1220 sig_io.sa_handler = CIMD_SigHandler;
1221 sig_io.sa_flags = 0;
1222 sigaction (SIGIO, &sig_io, NULL);
1227 result = device_open(PortDevice, false/*with_odd_parity*/, true/*with_async*/, -1/*with_hw_handshake*/, connection);
1230 perror(_("Couldn't open CIMD device"));
1235 /* create a thread to handle incoming data from mobile phone */
1236 rtn = pthread_create(&selThread, NULL, (void*)CIMD_SelectLoop, (void*)NULL);
1237 if (rtn != 0) return false;
1240 /* device_changespeed() not needed as device_open() now automatically
1241 * sets the user-specified (or default) speed.
1246 static void CIMD_SigHandler(int status)
1248 unsigned char buffer[255];
1250 res = device_read(buffer, 255);
1251 for (count = 0; count < res ; count ++)
1252 CIMD_RX_Char(buffer[count]);
1256 static bool CIMD_RX_Patrol_Ack(void)
1258 if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i) { /* ==0x99 */
1259 /* We cannot send the keepalive here as some dangerous operation may be in progress
1261 CIMD_KeepAlivesLockInit();
1262 pthread_mutex_lock(&CIMD_KeepAlivesLock);
1264 pthread_mutex_unlock(&CIMD_KeepAlivesLock);
1268 if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1269 return(false); /* Ack for some unknown command */
1271 free(CIMD_RX_Patrol_Ack_Error);
1272 CIMD_RX_Patrol_Ack_Error=strdup(CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Ack_Error)->u.StringZ.s);
1274 /* Some patrol may have already indicated some error!
1276 if (CIMD_CatchBufferErrorP && *CIMD_CatchBufferErrorP==GE_BUSY)
1277 *CIMD_CatchBufferErrorP=GE_NONE;
1282 static void CIMD_RX_Patrol_Ack_reset(void)
1284 free(CIMD_RX_Patrol_Ack_Error);
1285 CIMD_RX_Patrol_Ack_Error=NULL;
1288 static const char *CIMD_RX_Patrol_Nak_resolve(u16 errorcode)
1290 const struct CIMD_Param_Ack_Nak_Error *errorp;
1292 for (errorp=CIMD_Param_Ack_Nak_Error;errorp<CIMD_Param_Ack_Nak_Error+ARRAY_LEN(CIMD_Param_Ack_Nak_Error);errorp++)
1293 if (errorcode==errorp->code)
1294 return(_(errorp->msg));
1295 return(_("Unknown error code"));
1298 static void CIMD_RX_Patrol_Nak_dump(u16 errorcode)
1300 fprintf(stderr,_("Got CIMD error code 0x%04X: "),errorcode);
1301 if (!(errorcode & ~0x000F)) { /* combined error code */
1303 fprintf(stderr,_("combined:"));
1304 for (errormask=0x0001;errormask<=errorcode;errormask<<=1)
1305 if (errorcode&errormask)
1306 fprintf(stderr," +%s",CIMD_RX_Patrol_Nak_resolve(errormask));
1310 fprintf(stderr,"%s\n",CIMD_RX_Patrol_Nak_resolve(errorcode));
1313 static bool CIMD_RX_Patrol_Nak(void)
1315 if ((CIMD_Cmd+(unsigned)CIMD_Cmd_Nak)->code == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i /* ==0x99 */
1316 && CIMD_NAK_RESEND == CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i) { /* 0x9999 */
1317 fprintf(stderr,_("WARNING: Requested to resend last packet!!\n"));
1318 CIMD_TX_SendCommandResend();
1322 if (CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Cmd)->u.HexByte.i!=(CIMD_Cmd+(unsigned)CIMD_TX_SendCommand_Cmd)->code)
1323 return(false); /* Nak for some unknown command */
1325 CIMD_RX_Patrol_Nak_Error=CIMD_RX_PACKET_PARAMSLOT(CIMD_Param_Nak_Error)->u.HexWord.i;
1326 if (CIMD_CatchBufferErrorP)
1327 *CIMD_CatchBufferErrorP=GE_INTERNALERROR;
1329 CIMD_RX_Patrol_Nak_dump(CIMD_RX_Patrol_Nak_Error);
1334 static void CIMD_RX_Patrol_Nak_reset(void)
1336 CIMD_RX_Patrol_Nak_Error=0;
1339 static const struct CIMD_RX_Patrol CIMD_RX_Patrols[]={
1340 { CIMD_Cmd_Ack,CIMD_RX_Patrol_Ack,CIMD_RX_Patrol_Ack_reset },
1341 { CIMD_Cmd_Nak,CIMD_RX_Patrol_Nak,CIMD_RX_Patrol_Nak_reset },
1345 static void CIMD_CatchBufferReset(void)
1347 CIMD_CatchBufferPtr=CIMD_CatchBuffer;
1350 static void CIMD_CatchBufferStart(GSM_Error *errorcodep)
1352 static GSM_Error err_trashcan;
1353 const struct CIMD_RX_Patrol *patrol;
1356 errorcodep=&err_trashcan;
1357 CIMD_CatchBufferErrorP=errorcodep;
1359 CIMD_CatchBufferReset();
1361 for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1366 static u8 *CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1370 static bool CIMD_RX_ProcessRawPacket_parseparam(struct CIMD_paramslot *slot)
1372 switch (slot->elf) {
1374 case CIMD_ELF_HexByte: {
1375 unsigned parsedbyte;
1376 if (0 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1377 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1378 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[2]
1381 if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedbyte))
1383 slot->u.HexByte.i=parsedbyte;
1384 CIMD_RX_ProcessRawPacket_parseparam_buf_s+=2+1;
1387 case CIMD_ELF_HexWord: {
1388 unsigned parsedword;
1389 if (0 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[0])
1390 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[1])
1391 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[2])
1392 || !isxdigit(CIMD_RX_ProcessRawPacket_parseparam_buf_s[3])
1393 || CIMD_MARK_SEP!=CIMD_RX_ProcessRawPacket_parseparam_buf_s[4]
1396 if (1!=sscanf(CIMD_RX_ProcessRawPacket_parseparam_buf_s,"%X" CIMD_MARK_SEPS,&parsedword))
1398 slot->u.HexWord.i=parsedword;
1399 CIMD_RX_ProcessRawPacket_parseparam_buf_s+=4+1;
1402 case CIMD_ELF_Decimal: {
1404 u8 *start=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1405 if (CIMD_MARK_SEP==*start)
1406 return(false); /* empty Decimal not permitted */
1407 while (isdigit(*CIMD_RX_ProcessRawPacket_parseparam_buf_s))
1408 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1409 if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++) /* skip CIMD_MARK_SEP */
1411 if (1!=sscanf(start,"%d" CIMD_MARK_SEPS,&parsedint))
1413 slot->u.Decimal.i=parsedint;
1416 case CIMD_ELF_StringZ:
1417 slot->u.StringZ.s=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1418 while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1419 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1420 *CIMD_RX_ProcessRawPacket_parseparam_buf_s++='\0'; /* skip CIMD_MARK_SEP */
1423 case CIMD_ELF_HexBlock:
1424 slot->u.HexBlock.buf=CIMD_RX_ProcessRawPacket_parseparam_buf_s;
1426 while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1427 CIMD_RX_ProcessRawPacket_parseparam_buf_s++;
1428 if ((CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf)&1)
1429 return(false); /* odd number of xdigits */
1431 slot->u.HexBlock.len=(CIMD_RX_ProcessRawPacket_parseparam_buf_s-slot->u.HexBlock.buf);
1432 if (!SMS_BlockFromHex(slot->u.HexBlock.buf/*d*/,slot->u.HexBlock.buf/*s*/,slot->u.HexBlock.len*2))
1433 return(false); /* parse error */
1435 CIMD_RX_ProcessRawPacket_parseparam_buf_s++; /* skip CIMD_MARK_SEP */
1438 case CIMD_ELF_Empty:
1439 if (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s)
1440 return(false); /* some content found */
1443 return(true); /* success */
1447 static bool CIMD_RX_EvalPatrol(const struct CIMD_RX_Patrol *patrol)
1450 return(false); /* not recognized */
1451 if (CIMD_RX_Packet_Cmd!=patrol->cmd)
1452 return(false); /* not recognized */
1453 return((*patrol->func)());
1456 static inline void CIMD_RX_ProcessPacket(void)
1458 const struct CIMD_RX_Patrol *patrol;
1460 for (patrol=CIMD_RX_Patrols;patrol<CIMD_RX_Patrols+ARRAY_LEN(CIMD_RX_Patrols);patrol++)
1461 if (CIMD_RX_EvalPatrol(patrol))
1462 return; /* recognized and processed - whether successfuly is not interesting */
1463 CIMD_RX_EvalPatrol(CIMD_RX_Patrol_Current);
1466 static inline void CIMD_RX_ProcessRawPacket(void)
1471 const struct CIMD_Param *param;
1472 const struct CIMD_Cmd *cmdp;
1475 write(1,"GOT:<STX>",9);
1476 write(1,CIMD_CatchBuffer+1,CIMD_CatchBufferPtr-1-(CIMD_CatchBuffer+1));
1477 write(1,"<ETX>\n",6);
1480 if (CIMD_CatchBufferPtr<CIMD_CatchBuffer +1/*START*/ +1/*SEP*/ +2/*xsum*/ +1/*STOP*/)
1481 return; /* error - buffer too short */
1482 if (0 || CIMD_CatchBufferPtr[-4]!=CIMD_MARK_SEP
1483 || !isxdigit(CIMD_CatchBufferPtr[-3])
1484 || !isxdigit(CIMD_CatchBufferPtr[-2])
1486 return; /* error - invalid buffer tail */
1488 /* (*Ptr) will now point to the end of parameter part */
1489 CIMD_CatchBufferPtr-=3;
1490 if (1!=sscanf(CIMD_CatchBufferPtr,"%X" CIMD_MARK_SEPS,&xsumgot))
1491 return; /* INTERNAL - we have already checked the validity! */
1493 for (u8s=CIMD_CatchBuffer;u8s<CIMD_CatchBufferPtr;u8s++)
1496 return; /* error - invalid checksum */
1498 CIMD_RX_ProcessRawPacket_parseparam_buf_s=CIMD_CatchBuffer +1/*START*/;
1499 CIMD_RX_Packet_slots=1; /* just [0] now */
1500 CIMD_RX_Packet_slot[0].code=0; /* ignored now - position */
1501 CIMD_RX_Packet_slot[0].elf=CIMD_ELF_HexByte;
1502 if (!CIMD_RX_ProcessRawPacket_parseparam(CIMD_RX_Packet_slot+0))
1503 return; /* error - cmd code not parsed */
1505 for (cmdp=CIMD_Cmd;cmdp<CIMD_Cmd+CIMD_Cmdcnt;cmdp++)
1506 if (cmdp->code==CIMD_RX_Packet_slot[0].u.HexByte.i) break;
1507 if (cmdp>=CIMD_Cmd+CIMD_Cmdcnt)
1508 return; /* error - unknown command */
1509 CIMD_RX_Packet_Cmd=(enum CIMD_Cmd_Type)(cmdp-CIMD_Cmd);
1511 /* "position" handling is BIP dependency! */
1512 for (position=1;CIMD_RX_ProcessRawPacket_parseparam_buf_s<CIMD_CatchBufferPtr;position++) {
1513 struct CIMD_paramslot *slot;
1515 /* This code search is a crude overhead just for BIP now :-)
1517 for (param=cmdp->param;param<cmdp->param+cmdp->paramcnt;param++)
1518 if (position==param->code)
1520 if (param>=cmdp->param+cmdp->paramcnt) { /* not found - ignore */
1521 while (CIMD_MARK_SEP!=*CIMD_RX_ProcessRawPacket_parseparam_buf_s++); /* skip CIMD_MARK_SEP */
1524 slot=CIMD_RX_Packet_slot+1+(param-cmdp->param);
1525 slot->code=param->code;
1526 slot->elf=param->elf;
1527 if (!CIMD_RX_ProcessRawPacket_parseparam(slot))
1528 return; /* error - unable to parse required param */
1529 /* CIMD_MARK_SEP is skipped by CIMD_RX_ProcessRawPacket_parseparam() automatically
1531 CIMD_RX_Packet_slots++;
1533 /* The packet is now parsed */
1535 CIMD_RX_ProcessPacket();
1538 /* RX_State machine for receive handling. Called once for each character
1539 received from the phone/phone. */
1541 static void CIMD_RX_Char(char rx_byte)
1545 dprintf(_("Received character '%c' (0x%02X)\n"),
1546 (!isgraph(rx_byte) ? '?' : rx_byte),(unsigned char)rx_byte);
1551 /* NEVER store '\0' as it would knock-out completely our patrolling system!
1552 * It is invalid anyway
1554 if (rx_byte=='\0') {
1555 CIMD_CatchBufferReset();
1559 if (CIMD_CatchBufferPtr==CIMD_CatchBuffer && rx_byte!=CIMD_MARK_START) {
1560 /* No haven't yet catched any START yet */
1563 if (CIMD_CatchBufferPtr>=CIMD_CatchBuffer+sizeof(CIMD_CatchBuffer)) {
1564 fprintf(stderr,_("WARNING: Incoming CIMD packet too long to fit (>%d bytes)!\n"),sizeof(CIMD_CatchBuffer));
1565 CIMD_CatchBufferReset();
1568 *CIMD_CatchBufferPtr++=rx_byte;
1569 if (rx_byte==CIMD_MARK_STOP) {
1570 CIMD_RX_ProcessRawPacket();
1571 CIMD_CatchBufferReset();
1575 /* Here we initialise model specific functions. */
1577 #define CIMD_FUNCTIONS_ENTRY(name) \
1578 case GOP_##name: return(CIMD_##name(data,state));
1580 static GSM_Error CIMD_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *state)
1582 static GSM_Statemachine *catcher=NULL;
1584 if (!catcher || catcher==state)
1590 CIMD_FUNCTIONS_ENTRY(Init)
1591 CIMD_FUNCTIONS_ENTRY(Terminate)
1592 CIMD_FUNCTIONS_ENTRY(GetSMSStatus)
1593 CIMD_FUNCTIONS_ENTRY(GetSMS)
1594 CIMD_FUNCTIONS_ENTRY(SendSMS)
1595 CIMD_FUNCTIONS_ENTRY(GetImei)
1596 CIMD_FUNCTIONS_ENTRY(GetRevision)
1597 CIMD_FUNCTIONS_ENTRY(GetModel)
1598 CIMD_FUNCTIONS_ENTRY(GetManufacturer)
1599 CIMD_FUNCTIONS_ENTRY(Reset)
1601 case GOP_Identify: {
1602 GSM_Error err,r=GE_NONE;
1603 if (GE_NONE!=(err=CIMD_GetImei (data,state)))
1605 if (GE_NONE!=(err=CIMD_GetRevision (data,state)))
1607 if (GE_NONE!=(err=CIMD_GetModel (data,state)))
1609 if (GE_NONE!=(err=CIMD_GetManufacturer(data,state)))
1615 return(GE_NOTIMPLEMENTED);
1619 GSM_Phone phone_cimd = {
1620 UNIMPLEMENTED, /* IncomingFunctions - we don't use default StateMachine */
1621 UNIMPLEMENTED, /* DefaultFunction - we don't use default StateMachine */
1622 /* Mobile phone information */
1624 "BIP" /* |CIMD - not yet */, /* Supported models */
1625 100, /* Max RF Level (AT+CSQ) */
1626 0, /* Min RF Level (AT+CSQ) */
1627 GRF_Percentage, /* RF level units */
1628 100, /* Max Battery Level (AT+CBC) */
1629 0, /* Min Battery Level (AT+CBC) */
1630 GBU_Percentage, /* Battery level units */
1631 GDT_None, /* Have date/time support */
1632 GDT_None, /* Alarm supports time only */
1633 0, /* No alarm available */
1634 48, 84, /* Startup logo size */
1635 14, 72, /* Op logo size */
1636 14, 72, /* Caller logo size */