Update: orig2001_11_27_05_17 -> orig2001_12_04_22_45
[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 */
14
15 #include "gsm-common.h"
16 #include "gsm-statemachine.h"
17
18 GSM_Error SM_Initialise(GSM_Statemachine *state)
19 {
20         state->CurrentState = Initialised;
21         state->NumWaitingFor = 0;
22         state->NumReceived = 0; 
23         
24         return GE_NONE;
25 }
26
27 GSM_Error SM_SendMessage(GSM_Statemachine *state, u16 messagesize, u8 messagetype, void *message)
28 {
29         if (state->CurrentState != Startup) {
30                 state->LastMsgSize = messagesize;
31                 state->LastMsgType = messagetype;
32                 state->LastMsg = message;
33                 state->CurrentState = MessageSent;
34
35                 /* FIXME - clear KeepAlive timer */
36                 return state->Link.SendMessage(messagesize, messagetype, message);
37         }
38         else return GE_NOTREADY;
39 }
40
41 GSM_State SM_Loop(GSM_Statemachine *state, int timeout)
42 {
43         struct timeval loop_timeout;
44         int i;
45
46         loop_timeout.tv_sec = 0;
47         loop_timeout.tv_usec = 100000;
48
49         if (!state->Link.Loop) {
50                 dprintf("No Loop function. Aborting.\n");
51                 abort();
52         }
53         for (i = 0; i < timeout; i++) {
54                 state->Link.Loop(&loop_timeout);
55         }
56
57         /* FIXME - add calling a KeepAlive function here */
58         return state->CurrentState;
59 }
60
61 void SM_Reset(GSM_Statemachine *state)
62 {
63         /* Don't reset to initialised if we aren't! */
64         if (state->CurrentState != Startup) {
65                 state->CurrentState = Initialised;
66                 state->NumWaitingFor = 0;
67                 state->NumReceived = 0;       
68         }
69 }
70
71
72
73 void SM_IncomingFunction(GSM_Statemachine *state, u8 messagetype, void *message, u16 messagesize)
74 {
75         int c;
76         int temp = 1;
77         GSM_Data emptydata;
78         GSM_Data *data = &emptydata;
79         GSM_Error res = GE_INTERNALERROR;
80         int waitingfor = -1;
81
82         GSM_DataClear(&emptydata);
83
84         /* See if we need to pass the function the data struct */
85         if (state->CurrentState == WaitingForResponse)
86                 for (c = 0; c < state->NumWaitingFor; c++) 
87                         if (state->WaitingFor[c] == messagetype) {
88                                 data = state->Data[c];
89                                 waitingfor = c;
90                         }
91                        
92       
93         /* Pass up the message to the correct phone function, with data if necessary */
94         c = 0;
95         while (state->Phone.IncomingFunctions[c].Functions) {
96                 if (state->Phone.IncomingFunctions[c].MessageType == messagetype) {
97                         dprintf("Received message type %02x\n\r", messagetype);
98                         res = state->Phone.IncomingFunctions[c].Functions(messagetype, message, messagesize, data);
99                         temp = 0;
100                 }
101                 c++;
102         }
103         if (temp != 0) {
104                 dprintf("Unknown Frame Type %02x\n\r", messagetype);
105                 state->Phone.DefaultFunction(messagetype, message, messagesize);
106
107                 return;
108         }
109
110         if (state->CurrentState == WaitingForResponse) {
111                 /* Check if we were waiting for a response and we received it */
112                 if (waitingfor != -1) {
113                         state->ResponseError[waitingfor] = res;
114                         state->NumReceived++;
115                 }
116                 
117                 /* Check if all waitingfors have been received */
118                 if (state->NumReceived == state->NumWaitingFor) {
119                         state->CurrentState = ResponseReceived; 
120                 }
121                 
122         }
123 }
124
125
126 /* This returns the error recorded from the phone function and indicates collection */
127
128 GSM_Error SM_GetError(GSM_Statemachine *state, unsigned char messagetype)
129 {
130         int c, d;
131         GSM_Error error = GE_NOTREADY;
132         
133         if (state->CurrentState==ResponseReceived) {
134                 for(c = 0; c < state->NumReceived; c++)
135                         if (state->WaitingFor[c] == messagetype) {
136                                 error = state->ResponseError[c];
137                                 for(d = c + 1 ;d < state->NumReceived; d++){
138                                         state->ResponseError[d-1] = state->ResponseError[d];
139                                         state->WaitingFor[d-1] = state->WaitingFor[d];
140                                         state->Data[d-1] = state->Data[d];
141                                 }
142                                 state->NumReceived--;
143                                 state->NumWaitingFor--;
144                                 c--; /* For neatness continue in the correct place */
145                         }
146                 if (state->NumReceived == 0) {
147                         state->NumWaitingFor = 0;
148                         state->CurrentState = Initialised;
149                 }
150         }
151         
152         return error;
153 }
154
155
156
157 /* Indicate that the phone code is waiting for an response */
158 /* This does not actually wait! */
159
160 GSM_Error SM_WaitFor(GSM_Statemachine *state, GSM_Data *data, unsigned char messagetype)
161 {
162
163         /* If we've received a response, we have to call SM_GetError first */
164         if ((state->CurrentState == Startup) || (state->CurrentState == ResponseReceived))
165                 return GE_NOTREADY;
166         
167         if (state->NumWaitingFor == SM_MAXWAITINGFOR) return GE_NOTREADY;
168         state->WaitingFor[state->NumWaitingFor] = messagetype;
169         state->Data[state->NumWaitingFor] = data;
170         state->ResponseError[state->NumWaitingFor] = GE_BUSY;
171         state->NumWaitingFor++;
172         state->CurrentState = WaitingForResponse;
173
174         return GE_NONE;
175 }
176
177
178 /* This function is for convinience only */
179 /* It is called after SM_SendMessage and blocks until a response is received */
180
181 GSM_Error SM_Block(GSM_Statemachine *state, GSM_Data *data, int waitfor) 
182 {
183         int retry;
184         int timeout;
185         GSM_State s;
186         GSM_Error err;
187
188         for (retry = 0; retry < 3; retry++) {
189                 timeout = 30;
190                 err = SM_WaitFor(state, data, waitfor);
191                 if (err != GE_NONE) return err; 
192
193                 do {            /* ~3secs timeout */
194                         s = SM_Loop(state, 1);  /* Timeout=100ms */
195                         timeout--;
196                 } while ((timeout > 0) && (s == WaitingForResponse));
197                 
198                 if (s == ResponseReceived) return SM_GetError(state, waitfor);
199
200                 dprintf("SM_Block Retry - %d\n\r", retry);
201                 SM_Reset(state);
202                 if (retry<2) SM_SendMessage(state, state->LastMsgSize, state->LastMsgType, state->LastMsg);
203         }
204
205         return GE_TIMEOUT;
206 }
207
208
209 /* Just to do things neatly */
210
211 GSM_Error SM_Functions(GSM_Operation op, GSM_Data *data, GSM_Statemachine *sm)
212 {
213         if (!sm->Phone.Functions) {
214                 dprintf("Sorry, phone has not yet been converted to new style. Phone.Functions == NULL!\n");
215                 return GE_INTERNALERROR;
216         }
217         return sm->Phone.Functions(op, data, sm);
218 }