1 /* (c) 2002-2004 by Marcin Wiacek and Michal Cihar */
2
3 #include "../../gsmstate.h"
4
5 #if defined(GSM_ENABLE_AT) || defined(GSM_ENABLE_BLUEAT) || defined(GSM_ENABLE_IRDAAT) || defined(GSM_ENABLE_DKU2AT)
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10
11 #include "../../gsmcomon.h"
12 #include "at.h"
13
AT_WriteMessage(GSM_StateMachine * s,unsigned const char * buffer,size_t length,int type)14 static GSM_Error AT_WriteMessage (GSM_StateMachine *s, unsigned const char *buffer,
15 size_t length, int type)
16 {
17 size_t sent=0, i=0;
18 ssize_t write_data=0;
19
20 GSM_DumpMessageText(s, buffer, length, type);
21 GSM_DumpMessageBinary(s, buffer, length, type);
22
23 if (s->Protocol.Data.AT.FastWrite) {
24 while (sent != length) {
25 write_data = s->Device.Functions->WriteDevice(s,buffer + sent, length - sent);
26
27 if (write_data == 0 || write_data < 0) {
28 return ERR_DEVICEWRITEERROR;
29 }
30 sent += write_data;
31 }
32 } else {
33 for (i = 0; i < length; i++) {
34 /* For some phones like Siemens M20 we need to wait a little
35 * after writing each char. Possible reason: these phones
36 * can't receive so fast chars or there is bug here in Gammu */
37 write_data = s->Device.Functions->WriteDevice(s, buffer + i, 1);
38
39 if (write_data != 1) {
40 return ERR_DEVICEWRITEERROR;
41 }
42 usleep(1000);
43 }
44 usleep(400000);
45 }
46 return ERR_NONE;
47 }
48
49 typedef struct {
50 const char *text;
51 int lines;
52 GSM_Phone_RequestID requestid;
53 } SpecialAnswersStruct;
54
AT_StateMachine(GSM_StateMachine * s,unsigned char rx_char)55 GSM_Error AT_StateMachine(GSM_StateMachine *s, unsigned char rx_char)
56 {
57 GSM_Protocol_Message Msg2;
58 GSM_Protocol_ATData *d = &s->Protocol.Data.AT;
59 size_t i;
60
61 /* These are lines with end of "normal" answers */
62 static const char *StatusStrings[] = {
63 /* Standard AT */
64 "OK\r",
65 "ERROR\r",
66
67 /* AT with bad end of lines */
68 "OK\n",
69 "ERROR\n",
70
71 /* Standard GSM */
72 "+CME ERROR:",
73 "+CMS ERROR:",
74
75 /* Motorola A1200 */
76 "MODEM ERROR:",
77
78 /* Huawei */
79 "COMMAND NOT SUPPORT",
80
81 NULL};
82
83 /* Some info from phone can be inside "normal" answers
84 * It starts with strings written here
85 */
86 static const SpecialAnswersStruct SpecialAnswers[] = {
87 /* Standard GSM */
88 {"+CGREG:" ,1, ID_GetNetworkInfo},
89 /* Following has 2 lines in PDU mode, 1 line in TEXT ... */
90 {"+CBM:" ,2, ID_All},
91 {"+CMT:" ,2, ID_All},
92 {"+CMTI:" ,1, ID_All},
93 {"+CDS:" ,2, ID_All},
94 {"+CDSI:" ,1, ID_All},
95 {"+CREG:" ,1, ID_GetNetworkInfo},
96 {"+CUSD" ,1, ID_All},
97 {"+COLP" ,1, ID_All},
98 {"+CLIP" ,1, ID_All},
99 {"+CRING" ,1, ID_All},
100 {"+CCWA" ,1, ID_All},
101 {"+CLCC" ,1, ID_All},
102
103 /* Standard AT */
104 {"RING" ,1, ID_All},
105 {"NO CARRIER" ,1, ID_All},
106 {"NO ANSWER" ,1, ID_All},
107
108 /* GlobeTrotter */
109 {"_OSIGQ:" ,1, ID_All},
110 {"_OBS:" ,1, ID_All},
111
112 {"^SCN:" ,1, ID_All},
113
114 /* Sony-Ericsson */
115 {"*EBCA" ,1, ID_All},
116
117 /* Samsung binary transfer end */
118 {"SDNDCRC =" ,1, ID_All},
119 /* Samsung reply to SSHT in some cases */
120 {"SAMSUNG PTS DG Test", 1, ID_All},
121
122 /* Cross PD1101wi reply to almost anything */
123 {"NOT FOND ^,NOT CUSTOM AT", 1, ID_All},
124
125 /* Motorola banner */
126 {"+MBAN:" ,1, ID_All},
127
128 /* HSPA CORPORATION */
129 {"+ZEND" ,1, ID_All},
130
131 /* Huawei */
132 {"^RSSI:" ,1, ID_All}, /* ^RSSI:18 */
133 {"^HCSQ:" ,1, ID_All}, /* ^HCSQ:"WCDMA",39,29,45 */
134 {"^DSFLOWRPT:" ,1, ID_All}, /* ^DSFLOWRPT:00000124,00000082,00000EA6,0000000000012325,000000000022771D,0000BB80,0001F400 */
135 {"^BOOT:" ,1, ID_All}, /* ^BOOT:27710117,0,0,0,75 */
136 {"^MODE:" ,1, ID_All}, /* ^MODE:3,3 */
137 {"^CSNR:" ,1, ID_All}, /* ^CSNR:-93,-23 */
138 {"^HCSQ:" ,1, ID_All}, /* ^HCSQ:"LTE",59,50,161,24 */
139 {"^SRVST:" ,1, ID_All}, /* ^SRVST:0 */
140 {"^SIMST:" ,1, ID_All}, /* ^SIMST:1 */
141 {"^STIN:" ,1, ID_All}, /* ^STIN: 7, 0, 0 */
142
143 /* D-Link */
144 {"+SPNWNAME:" ,1, ID_All}, /* +SPNWNAME: "432", "11", "Mci", "Mci" */
145 {"+PSBEARER:" ,1, ID_All}, /* +PSBEARER: 24, 0 */
146
147 /* ONDA */
148 {"+ZUSIMR:" ,1, ID_All}, /* +ZUSIMR:2 */
149
150 /* Telit */
151 {"#STN:" ,1, ID_All}, /* #STN: 150,1,"" */
152
153 {NULL ,1, ID_All}};
154
155 /* We're starting new message */
156 if (d->Msg.Length == 0) {
157 /* Ignore leading CR, LF and ESC */
158 if (rx_char == 10 || rx_char == 13 || rx_char == 27) {
159 return ERR_NONE;
160 }
161 d->LineStart = 0;
162 }
163
164 /* Allocate more memory if needed */
165 if (d->Msg.BufferUsed < d->Msg.Length + 2) {
166 d->Msg.BufferUsed = d->Msg.Length + 200;
167 d->Msg.Buffer = (unsigned char *)realloc(d->Msg.Buffer,d->Msg.BufferUsed);
168 if (d->Msg.Buffer == NULL) {
169 return ERR_MOREMEMORY;
170 }
171 }
172
173 /* Store current char in the buffer */
174 d->Msg.Buffer[d->Msg.Length++] = rx_char;
175 d->Msg.Buffer[d->Msg.Length ] = 0;
176
177 /* Parse char */
178 switch (rx_char) {
179 case 0:
180 break;
181 case 10:
182 case 13:
183 /* Store line end (if we did not do it in last char */
184 if (! d->wascrlf) {
185 d->LineEnd = d->Msg.Length - 1;
186 }
187 d->wascrlf = TRUE;
188
189 /* Process line after \r\n */
190 if (d->Msg.Length > 0 && rx_char == 10 && d->Msg.Buffer[d->Msg.Length - 2] == 13) {
191 /* Process standard responses */
192 for (i = 0; StatusStrings[i] != NULL; i++) {
193 if (strncmp(StatusStrings[i],
194 d->Msg.Buffer + d->LineStart,
195 strlen(StatusStrings[i])) == 0) {
196 s->Phone.Data.RequestMsg = &d->Msg;
197 s->Phone.Data.DispatchError = s->Phone.Functions->DispatchMessage(s);
198 d->Msg.Length = 0;
199 break;
200 }
201 }
202 /* Generally hack for A2D */
203 if (d->CPINNoOK) {
204 if (strncmp("+CPIN: ",
205 d->Msg.Buffer + d->LineStart,
206 7) == 0) {
207 s->Phone.Data.RequestMsg = &d->Msg;
208 s->Phone.Data.DispatchError = s->Phone.Functions->DispatchMessage(s);
209 d->Msg.Length = 0;
210 break;
211 }
212 }
213
214 /* Check for incoming frames */
215 for (i = 0; SpecialAnswers[i].text != NULL; i++) {
216 if (strncmp(SpecialAnswers[i].text,
217 d->Msg.Buffer + d->LineStart,
218 strlen(SpecialAnswers[i].text)) == 0) {
219 /* We need something better here */
220 if (s->Phone.Data.RequestID == SpecialAnswers[i].requestid) {
221 i++;
222 continue;
223 }
224 if ((s->Phone.Data.RequestID == ID_SetOBEX || s->Phone.Data.RequestID == ID_DialVoice)&&
225 strcmp(SpecialAnswers[i].text, "NO CARRIER") == 0) {
226 i++;
227 continue;
228 }
229 d->SpecialAnswerStart = d->LineStart;
230 d->SpecialAnswerLines = SpecialAnswers[i].lines;
231 }
232 }
233
234 /* Last line of incoming frame */
235 if (d->SpecialAnswerLines == 1) {
236 /* This is end of special answer. We copy it and send to phone module */
237 Msg2.Buffer = (unsigned char *)malloc(d->LineEnd - d->SpecialAnswerStart + 3);
238 memcpy(Msg2. Buffer, d->Msg.Buffer + d->SpecialAnswerStart, d->LineEnd - d->SpecialAnswerStart + 2);
239 Msg2.Length = d->LineEnd - d->SpecialAnswerStart + 2;
240 Msg2.Buffer[Msg2.Length] = '\0';
241 Msg2.Type = 0;
242
243 s->Phone.Data.RequestMsg = &Msg2;
244 s->Phone.Data.DispatchError = s->Phone.Functions->DispatchMessage(s);
245 free(Msg2.Buffer);
246 Msg2.Buffer = NULL;
247
248 /* We cut special answer from main buffer */
249 d->Msg.Length = d->SpecialAnswerStart;
250
251 /* We need to find earlier values of all variables */
252 d->wascrlf = FALSE;
253 d->LineStart = 0;
254 for (i = 0;i < d->Msg.Length; i++) {
255 switch (d->Msg.Buffer[i]) {
256 case 0:
257 break;
258 case 10:
259 case 13:
260 if (!d->wascrlf) {
261 d->LineEnd = d->Msg.Length;
262 }
263 d->wascrlf = TRUE;
264 break;
265 default:
266 if (d->wascrlf) {
267 d->LineStart = d->Msg.Length;
268 d->wascrlf = FALSE;
269 }
270 }
271 }
272 d->Msg.Buffer[d->Msg.Length] = 0;
273 }
274 if (d->SpecialAnswerLines > 0) {
275 d->SpecialAnswerLines--;
276 }
277 }
278 break;
279 case 'T':
280 /* When CONNECT string received, we know there will not follow
281 * anything AT related, after CONNECT can follow ppp data, alcabus
282 * data and also other things.
283 */
284 if (strncmp(d->Msg.Buffer + d->LineStart, "CONNECT", 7) == 0) {
285 s->Phone.Data.RequestMsg = &d->Msg;
286 s->Phone.Data.DispatchError = s->Phone.Functions->DispatchMessage(s);
287 d->LineStart = -1;
288 d->Msg.Length = 0;
289 break;
290 }
291 FALLTHROUGH
292 default:
293 if (d->wascrlf) {
294 d->LineStart = d->Msg.Length - 1;
295 d->wascrlf = FALSE;
296 }
297 if (d->EditMode) {
298 if (strlen(d->Msg.Buffer+d->LineStart) == 2 &&
299 strncmp(d->Msg. Buffer + d->LineStart, "> ", 2) == 0) {
300 s->Phone.Data.RequestMsg = &d->Msg;
301 s->Phone.Data.DispatchError = s->Phone.Functions->DispatchMessage(s);
302 }
303 }
304 }
305 return ERR_NONE;
306 }
307
AT_Initialise(GSM_StateMachine * s)308 GSM_Error AT_Initialise(GSM_StateMachine *s)
309 {
310 GSM_Protocol_ATData *d = &s->Protocol.Data.AT;
311 GSM_Error error;
312
313 d->Msg.Buffer = NULL;
314 d->Msg.BufferUsed = 0;
315 d->Msg.Length = 0;
316 d->Msg.Type = 0;
317
318 d->SpecialAnswerLines = 0;
319 d->LineStart = -1;
320 d->LineEnd = -1;
321 d->wascrlf = FALSE;
322 d->EditMode = FALSE;
323 /* Slow write makes sense only on cable for some phones */
324 d->FastWrite = (s->ConnectionType != GCT_AT);
325 d->CPINNoOK = FALSE;
326
327 error = s->Device.Functions->DeviceSetParity(s, FALSE);
328 if (error != ERR_NONE) return error;
329
330 error = s->Device.Functions->DeviceSetDtrRts(s, TRUE, TRUE);
331 if (error != ERR_NONE) return error;
332
333 return s->Device.Functions->DeviceSetSpeed(s, s->Speed);
334 }
335
AT_Terminate(GSM_StateMachine * s)336 static GSM_Error AT_Terminate(GSM_StateMachine *s)
337 {
338 free(s->Protocol.Data.AT.Msg.Buffer);
339 s->Protocol.Data.AT.Msg.Buffer = NULL;
340 return ERR_NONE;
341 }
342
343 GSM_Protocol_Functions ATProtocol = {
344 AT_WriteMessage,
345 AT_StateMachine,
346 AT_Initialise,
347 AT_Terminate
348 };
349
350 #endif
351
352 /* How should editor hadle tabs in this file? Add editor commands here.
353 * vim: noexpandtab sw=8 ts=8 sts=8:
354 */
355