/* $Id$ G N O K I I A Linux/Unix toolset and driver for Nokia mobile phones. Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml. Released under the terms of the GNU GPL, see file COPYING for more details. $Log$ Revision 1.1.1.4 2002/04/03 00:07:57 short Found in "gnokii-working" directory, some November-patches version Revision 1.5 2001/09/09 21:45:49 machek Cleanups from Ladislav Michl : *) do *not* internationalize debug messages *) some whitespace fixes, do not use // *) break is unneccessary after return Revision 1.4 2001/07/27 00:02:20 pkot Generic AT support for the new structure (Manfred Jonsson) Revision 1.3 2001/06/10 11:26:56 machek Warn if Link.Loop is not defined. Revision 1.2 2001/05/07 14:01:51 machek Warn when phone functions are missing, but do not segfault. Revision 1.1 2001/03/21 23:36:04 chris Added the statemachine This will break gnokii --identify and --monitor except for 6210/7110 */ #include "gsm-common.h" #include "gsm-statemachine.h" #include "gsm-api.h" GSM_Error SM_Initialise(GSM_Statemachine *state) { state->CurrentState=Initialised; state->NumWaitingFor=0; state->NumReceived=0; return GE_NONE; } GSM_Error SM_SendMessage(GSM_Statemachine *state, u16 messagesize, u8 messagetype, void *message) { if (state->CurrentState!=Startup) { state->LastMsgSize=messagesize; state->LastMsgType=messagetype; state->LastMsg=message; state->CurrentState=MessageSent; /* FIXME - clear KeepAlive timer */ return state->Link.SendMessage(messagesize, messagetype, message); } else return GE_NOTREADY; } GSM_State SM_Loop(GSM_Statemachine *state, int timeout) { struct timeval loop_timeout; int i; loop_timeout.tv_sec = 0; loop_timeout.tv_usec = 100000; if (!state->Link.Loop) { fprintf(stderr, "No Loop function. Aborting.\n"); abort(); } for (i = 0; i < timeout; i++) { state->Link.Loop(&loop_timeout); } /* FIXME - add calling a KeepAlive function here */ return state->CurrentState; } void SM_Reset(GSM_Statemachine *state) { /* Don't reset to initialised if we aren't! */ if (state->CurrentState!=Startup) { state->CurrentState=Initialised; state->NumWaitingFor=0; state->NumReceived=0; } } void SM_IncomingFunction(GSM_Statemachine *state, u8 messagetype, void *message, u16 messagesize) { int c; int temp=1; GSM_Data emptydata; GSM_Data *data=&emptydata; GSM_Error res=GE_INTERNALERROR; int waitingfor=-1; GSM_DataClear(&emptydata); /* See if we need to pass the function the data struct */ if (state->CurrentState==WaitingForResponse) for (c=0; cNumWaitingFor; c++) if (state->WaitingFor[c]==messagetype) { data=state->Data[c]; waitingfor=c; } /* Pass up the message to the correct phone function, with data if necessary */ c=0; while (state->Phone.IncomingFunctions[c].Functions) { if (state->Phone.IncomingFunctions[c].MessageType == messagetype) { dprintf("Received message type %02x\n\r", messagetype); res=state->Phone.IncomingFunctions[c].Functions(messagetype, message, messagesize, data); temp=0; } c++; } if (temp != 0) { dprintf("Unknown Frame Type %02x\n\r", messagetype); state->Phone.DefaultFunction(messagetype, message, messagesize); return; } if (state->CurrentState==WaitingForResponse) { /* Check if we were waiting for a response and we received it */ if (waitingfor!=-1) { state->ResponseError[waitingfor]=res; state->NumReceived++; } /* Check if all waitingfors have been received */ if (state->NumReceived==state->NumWaitingFor) { state->CurrentState=ResponseReceived; } } } /* This returns the error recorded from the phone function and indicates collection */ GSM_Error SM_GetError(GSM_Statemachine *state, unsigned char messagetype) { int c,d; GSM_Error error=GE_NOTREADY; if (state->CurrentState==ResponseReceived) { for(c=0; c < state->NumReceived; c++) if (state->WaitingFor[c]==messagetype) { error=state->ResponseError[c]; for( d=c+1 ; d < state->NumReceived; d++){ state->ResponseError[d-1]=state->ResponseError[d]; state->WaitingFor[d-1]=state->WaitingFor[d]; state->Data[d-1]=state->Data[d]; } state->NumReceived--; state->NumWaitingFor--; c--; /* For neatness continue in the correct place */ } if (state->NumReceived==0) { state->NumWaitingFor=0; state->CurrentState=Initialised; } } return error; } /* Indicate that the phone code is waiting for an response */ /* This does not actually wait! */ GSM_Error SM_WaitFor(GSM_Statemachine *state, GSM_Data *data, unsigned char messagetype) { /* If we've received a response, we have to call SM_GetError first */ if ((state->CurrentState==Startup) || (state->CurrentState==ResponseReceived)) return GE_NOTREADY; if (state->NumWaitingFor==SM_MAXWAITINGFOR) return GE_NOTREADY; ; state->WaitingFor[state->NumWaitingFor]=messagetype; state->Data[state->NumWaitingFor]=data; state->NumWaitingFor++; state->CurrentState=WaitingForResponse; return GE_NONE; } /* This function is for convinience only */ /* It is called after SM_SendMessage and blocks until a response is received */ GSM_Error SM_Block(GSM_Statemachine *state, GSM_Data *data, int waitfor) { int retry; int timeout; GSM_State s; GSM_Error err; for (retry=0; retry<3; retry++) { timeout=30; err=SM_WaitFor(state,data,waitfor); if (err!=GE_NONE) return err; do { /* ~3secs timeout */ s=SM_Loop(state, 1); /* Timeout=100ms */ timeout--; } while ((timeout>0) && (s==WaitingForResponse)); if (s==ResponseReceived) return SM_GetError(state, waitfor); dprintf("SM_Block Retry - %d\n\r", retry); SM_Reset(state); if (retry<2) SM_SendMessage(state, state->LastMsgSize, state->LastMsgType, state->LastMsg); } return GE_TIMEOUT; } /* Just to do things neatly */ GSM_Error SM_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *sm) { GSM_Error err; if (!sm->Phone.Functions) err=GE_NOTIMPLEMENTED; else err=sm->Phone.Functions(op, data, sm); if (err==GE_NOTIMPLEMENTED) err=compat_Phone_Functions(op, data, sm); return(err); }