:pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
[gnokii.git] / common / gsm-statemachine.c
1 /*
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12
13   $Log$
14   Revision 1.1.1.1  2001/11/25 21:59:01  short
15   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
16
17   Revision 1.7  2001/11/17 20:16:42  pkot
18   Cleanup
19
20   Revision 1.6  2001/11/08 16:49:19  pkot
21   Cleanups
22
23   Revision 1.5  2001/09/09 21:45:49  machek
24   Cleanups from Ladislav Michl <ladis@psi.cz>:
25
26   *) do *not* internationalize debug messages
27
28   *) some whitespace fixes, do not use //
29
30   *) break is unneccessary after return
31
32   Revision 1.4  2001/07/27 00:02:20  pkot
33   Generic AT support for the new structure (Manfred Jonsson)
34
35   Revision 1.3  2001/06/10 11:26:56  machek
36   Warn if Link.Loop is not defined.
37
38   Revision 1.2  2001/05/07 14:01:51  machek
39   Warn when phone functions are missing, but do not segfault.
40
41   Revision 1.1  2001/03/21 23:36:04  chris
42   Added the statemachine
43   This will break gnokii --identify and --monitor except for 6210/7110
44
45
46 */
47
48 #include "gsm-common.h"
49 #include "gsm-statemachine.h"
50
51 GSM_Error SM_Initialise(GSM_Statemachine *state)
52 {
53         state->CurrentState = Initialised;
54         state->NumWaitingFor = 0;
55         state->NumReceived = 0; 
56         
57         return GE_NONE;
58 }
59
60 GSM_Error SM_SendMessage(GSM_Statemachine *state, u16 messagesize, u8 messagetype, void *message)
61 {
62         if (state->CurrentState != Startup) {
63                 state->LastMsgSize = messagesize;
64                 state->LastMsgType = messagetype;
65                 state->LastMsg = message;
66                 state->CurrentState = MessageSent;
67
68                 /* FIXME - clear KeepAlive timer */
69                 return state->Link.SendMessage(messagesize, messagetype, message);
70         }
71         else return GE_NOTREADY;
72 }
73
74 GSM_State SM_Loop(GSM_Statemachine *state, int timeout)
75 {
76         struct timeval loop_timeout;
77         int i;
78
79         loop_timeout.tv_sec = 0;
80         loop_timeout.tv_usec = 100000;
81
82         if (!state->Link.Loop) {
83                 dprintf("No Loop function. Aborting.\n");
84                 abort();
85         }
86         for (i = 0; i < timeout; i++) {
87                 state->Link.Loop(&loop_timeout);
88         }
89
90         /* FIXME - add calling a KeepAlive function here */
91         return state->CurrentState;
92 }
93
94 void SM_Reset(GSM_Statemachine *state)
95 {
96         /* Don't reset to initialised if we aren't! */
97         if (state->CurrentState != Startup) {
98                 state->CurrentState = Initialised;
99                 state->NumWaitingFor = 0;
100                 state->NumReceived = 0;       
101         }
102 }
103
104
105
106 void SM_IncomingFunction(GSM_Statemachine *state, u8 messagetype, void *message, u16 messagesize)
107 {
108         int c;
109         int temp = 1;
110         GSM_Data emptydata;
111         GSM_Data *data = &emptydata;
112         GSM_Error res = GE_INTERNALERROR;
113         int waitingfor = -1;
114
115         GSM_DataClear(&emptydata);
116
117         /* See if we need to pass the function the data struct */
118         if (state->CurrentState == WaitingForResponse)
119                 for (c = 0; c < state->NumWaitingFor; c++) 
120                         if (state->WaitingFor[c] == messagetype) {
121                                 data = state->Data[c];
122                                 waitingfor = c;
123                         }
124                        
125       
126         /* Pass up the message to the correct phone function, with data if necessary */
127         c = 0;
128         while (state->Phone.IncomingFunctions[c].Functions) {
129                 if (state->Phone.IncomingFunctions[c].MessageType == messagetype) {
130                         dprintf("Received message type %02x\n\r", messagetype);
131                         res = state->Phone.IncomingFunctions[c].Functions(messagetype, message, messagesize, data);
132                         temp = 0;
133                 }
134                 c++;
135         }
136         if (temp != 0) {
137                 dprintf("Unknown Frame Type %02x\n\r", messagetype);
138                 state->Phone.DefaultFunction(messagetype, message, messagesize);
139
140                 return;
141         }
142
143         if (state->CurrentState == WaitingForResponse) {
144                 /* Check if we were waiting for a response and we received it */
145                 if (waitingfor != -1) {
146                         state->ResponseError[waitingfor] = res;
147                         state->NumReceived++;
148                 }
149                 
150                 /* Check if all waitingfors have been received */
151                 if (state->NumReceived == state->NumWaitingFor) {
152                         state->CurrentState = ResponseReceived; 
153                 }
154                 
155         }
156 }
157
158
159 /* This returns the error recorded from the phone function and indicates collection */
160
161 GSM_Error SM_GetError(GSM_Statemachine *state, unsigned char messagetype)
162 {
163         int c, d;
164         GSM_Error error = GE_NOTREADY;
165         
166         if (state->CurrentState==ResponseReceived) {
167                 for(c = 0; c < state->NumReceived; c++)
168                         if (state->WaitingFor[c] == messagetype) {
169                                 error = state->ResponseError[c];
170                                 for(d = c + 1 ;d < state->NumReceived; d++){
171                                         state->ResponseError[d-1] = state->ResponseError[d];
172                                         state->WaitingFor[d-1] = state->WaitingFor[d];
173                                         state->Data[d-1] = state->Data[d];
174                                 }
175                                 state->NumReceived--;
176                                 state->NumWaitingFor--;
177                                 c--; /* For neatness continue in the correct place */
178                         }
179                 if (state->NumReceived == 0) {
180                         state->NumWaitingFor = 0;
181                         state->CurrentState = Initialised;
182                 }
183         }
184         
185         return error;
186 }
187
188
189
190 /* Indicate that the phone code is waiting for an response */
191 /* This does not actually wait! */
192
193 GSM_Error SM_WaitFor(GSM_Statemachine *state, GSM_Data *data, unsigned char messagetype)
194 {
195
196         /* If we've received a response, we have to call SM_GetError first */
197         if ((state->CurrentState == Startup) || (state->CurrentState == ResponseReceived))
198                 return GE_NOTREADY;
199         
200         if (state->NumWaitingFor == SM_MAXWAITINGFOR) return GE_NOTREADY;
201         state->WaitingFor[state->NumWaitingFor] = messagetype;
202         state->Data[state->NumWaitingFor] = data;
203         state->ResponseError[state->NumWaitingFor] = GE_BUSY;
204         state->NumWaitingFor++;
205         state->CurrentState = WaitingForResponse;
206
207         return GE_NONE;
208 }
209
210
211 /* This function is for convinience only */
212 /* It is called after SM_SendMessage and blocks until a response is received */
213
214 GSM_Error SM_Block(GSM_Statemachine *state, GSM_Data *data, int waitfor) 
215 {
216         int retry;
217         int timeout;
218         GSM_State s;
219         GSM_Error err;
220
221         for (retry = 0; retry < 3; retry++) {
222                 timeout = 30;
223                 err = SM_WaitFor(state, data, waitfor);
224                 if (err != GE_NONE) return err; 
225
226                 do {            /* ~3secs timeout */
227                         s = SM_Loop(state, 1);  /* Timeout=100ms */
228                         timeout--;
229                 } while ((timeout > 0) && (s == WaitingForResponse));
230                 
231                 if (s == ResponseReceived) return SM_GetError(state, waitfor);
232
233                 dprintf("SM_Block Retry - %d\n\r", retry);
234                 SM_Reset(state);
235                 if (retry<2) SM_SendMessage(state, state->LastMsgSize, state->LastMsgType, state->LastMsg);
236         }
237
238         return GE_TIMEOUT;
239 }
240
241
242 /* Just to do things neatly */
243
244 GSM_Error SM_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *sm)
245 {
246         if (!sm->Phone.Functions) {
247                 dprintf("Sorry, phone has not yet been converted to new style. Phone.Functions == NULL!\n");
248                 return GE_INTERNALERROR;
249         }
250         return sm->Phone.Functions(op, data, sm);
251 }