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