Fix a problem with CtrlZ echo
[framework/connectivity/connman.git] / gatchat / gatsyntax.c
1 /*
2  *
3  *  AT chat library with GLib integration
4  *
5  *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27
28 #include "gatsyntax.h"
29
30 enum GSMV1_STATE_ {
31         GSMV1_STATE_IDLE = 0,
32         GSMV1_STATE_INITIAL_CR,
33         GSMV1_STATE_INITIAL_LF,
34         GSMV1_STATE_RESPONSE,
35         GSMV1_STATE_TERMINATOR_CR,
36         GSMV1_STATE_GUESS_MULTILINE_RESPONSE,
37         GSMV1_STATE_MULTILINE_RESPONSE,
38         GSMV1_STATE_MULTILINE_TERMINATOR_CR,
39         GSMV1_STATE_PDU_CHECK_EXTRA_CR,
40         GSMV1_STATE_PDU_CHECK_EXTRA_LF,
41         GSMV1_STATE_PDU,
42         GSMV1_STATE_PDU_CR,
43         GSMV1_STATE_PROMPT,
44         GSMV1_STATE_GARBAGE,
45         GSMV1_STATE_GARBAGE_CHECK_LF,
46 };
47
48 static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
49 {
50         switch (hint) {
51         case G_AT_SYNTAX_EXPECT_PDU:
52                 syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_CR;
53                 break;
54         case G_AT_SYNTAX_EXPECT_MULTILINE:
55                 syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE;
56                 break;
57         default:
58                 break;
59         };
60 }
61
62 static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax,
63                                         const char *bytes, gsize *len)
64 {
65         gsize i = 0;
66         GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
67
68         while (i < *len) {
69                 char byte = bytes[i];
70
71                 switch (syntax->state) {
72                 case GSMV1_STATE_IDLE:
73                         if (byte == '\r')
74                                 syntax->state = GSMV1_STATE_INITIAL_CR;
75                         else
76                                 syntax->state = GSMV1_STATE_GARBAGE;
77                         break;
78
79                 case GSMV1_STATE_INITIAL_CR:
80                         if (byte == '\n')
81                                 syntax->state = GSMV1_STATE_INITIAL_LF;
82                         else
83                                 syntax->state = GSMV1_STATE_GARBAGE;
84                         break;
85
86                 case GSMV1_STATE_INITIAL_LF:
87                         if (byte == '\r')
88                                 syntax->state = GSMV1_STATE_TERMINATOR_CR;
89                         else if (byte == '>')
90                                 syntax->state = GSMV1_STATE_PROMPT;
91                         else
92                                 syntax->state = GSMV1_STATE_RESPONSE;
93                         break;
94
95                 case GSMV1_STATE_RESPONSE:
96                         if (byte == '\r')
97                                 syntax->state = GSMV1_STATE_TERMINATOR_CR;
98                         break;
99
100                 case GSMV1_STATE_TERMINATOR_CR:
101                         syntax->state = GSMV1_STATE_IDLE;
102
103                         if (byte == '\n') {
104                                 i += 1;
105                                 res = G_AT_SYNTAX_RESULT_LINE;
106                         } else
107                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
108
109                         goto out;
110
111                 case GSMV1_STATE_GUESS_MULTILINE_RESPONSE:
112                         if (byte == '\r')
113                                 syntax->state = GSMV1_STATE_INITIAL_CR;
114                         else
115                                 syntax->state = GSMV1_STATE_MULTILINE_RESPONSE;
116                         break;
117
118                 case GSMV1_STATE_MULTILINE_RESPONSE:
119                         if (byte == '\r')
120                                 syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR;
121                         break;
122
123                 case GSMV1_STATE_MULTILINE_TERMINATOR_CR:
124                         syntax->state = GSMV1_STATE_IDLE;
125
126                         if (byte == '\n') {
127                                 i += 1;
128                                 res = G_AT_SYNTAX_RESULT_MULTILINE;
129                         } else
130                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
131
132                         goto out;
133
134                 /* Some 27.007 compliant modems still get this wrong.  They
135                  * insert an extra CRLF between the command and he PDU,
136                  * in effect making them two separate lines.  We try to
137                  * handle this case gracefully
138                  */
139                 case GSMV1_STATE_PDU_CHECK_EXTRA_CR:
140                         if (byte == '\r')
141                                 syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_LF;
142                         else
143                                 syntax->state = GSMV1_STATE_PDU;
144                         break;
145
146                 case GSMV1_STATE_PDU_CHECK_EXTRA_LF:
147                         res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
148                         syntax->state = GSMV1_STATE_PDU;
149
150                         if (byte == '\n')
151                                 i += 1;
152
153                         goto out;
154
155                 case GSMV1_STATE_PDU:
156                         if (byte == '\r')
157                                 syntax->state = GSMV1_STATE_PDU_CR;
158                         break;
159
160                 case GSMV1_STATE_PDU_CR:
161                         syntax->state = GSMV1_STATE_IDLE;
162
163                         if (byte == '\n') {
164                                 i += 1;
165                                 res = G_AT_SYNTAX_RESULT_PDU;
166                         } else
167                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
168
169                         goto out;
170
171                 case GSMV1_STATE_PROMPT:
172                         if (byte == ' ') {
173                                 syntax->state = GSMV1_STATE_IDLE;
174                                 i += 1;
175                                 res = G_AT_SYNTAX_RESULT_PROMPT;
176                                 goto out;
177                         }
178
179                         syntax->state = GSMV1_STATE_RESPONSE;
180                         return G_AT_SYNTAX_RESULT_UNSURE;
181
182                 case GSMV1_STATE_GARBAGE:
183                         if (byte == '\r')
184                                 syntax->state = GSMV1_STATE_GARBAGE_CHECK_LF;
185                         /* This handles the case of echo of the PDU terminated
186                          * by CtrlZ character
187                          */
188                         else if (byte == 26) {
189                                 syntax->state = GSMV1_STATE_IDLE;
190                                 res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
191                                 i += 1;
192                                 goto out;
193                         }
194
195                         break;
196
197                 case GSMV1_STATE_GARBAGE_CHECK_LF:
198                         syntax->state = GSMV1_STATE_IDLE;
199                         res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
200
201                         if (byte == '\n')
202                                 i += 1;
203
204                         goto out;
205
206                 default:
207                         break;
208                 };
209
210                 i += 1;
211         }
212
213 out:
214         *len = i;
215         return res;
216 }
217
218 GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
219                                         GAtSyntaxSetHintFunc hint,
220                                         int initial_state)
221 {
222         GAtSyntax *syntax;
223
224         syntax = g_new0(GAtSyntax, 1);
225
226         syntax->feed = feed;
227         syntax->set_hint = hint;
228         syntax->state = initial_state;
229         syntax->ref_count = 1;
230
231         return syntax;
232 }
233
234
235 GAtSyntax *g_at_syntax_new_gsmv1()
236 {
237         return g_at_syntax_new_full(gsmv1_feed, gsmv1_hint, GSMV1_STATE_IDLE);
238 }
239
240 GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax)
241 {
242         if (syntax == NULL)
243                 return NULL;
244
245         g_atomic_int_inc(&syntax->ref_count);
246
247         return syntax;
248 }
249
250 void g_at_syntax_unref(GAtSyntax *syntax)
251 {
252         gboolean is_zero;
253
254         if (syntax == NULL)
255                 return;
256
257         is_zero = g_atomic_int_dec_and_test(&syntax->ref_count);
258
259         if (is_zero)
260                 g_free(syntax);
261 }