summaryrefslogtreecommitdiff
path: root/gatchat
diff options
context:
space:
mode:
authorDenis Kenzior <denkenz@gmail.com>2009-08-04 18:53:25 -0500
committerMarcel Holtmann <marcel@holtmann.org>2009-08-06 17:48:48 -0700
commit82ccfae22a5d1638870d283ecf953d9b2a8a184f (patch)
treeaf398913ad121db85a0179d8c1081cc4eb45b10f /gatchat
parentacfb52bb7e18a227a5c141af9e112ad8f4255a0b (diff)
downloadconnman-82ccfae22a5d1638870d283ecf953d9b2a8a184f.tar.gz
connman-82ccfae22a5d1638870d283ecf953d9b2a8a184f.tar.bz2
connman-82ccfae22a5d1638870d283ecf953d9b2a8a184f.zip
Refactor GAtChat to accept user-provided parsers
Intended for really broken modems. A default 27.007 compliant parser is provided.
Diffstat (limited to 'gatchat')
-rw-r--r--gatchat/Makefile.am2
-rw-r--r--gatchat/gatchat.c304
-rw-r--r--gatchat/gatchat.h8
-rw-r--r--gatchat/gatsyntax.c228
-rw-r--r--gatchat/gatsyntax.h75
5 files changed, 387 insertions, 230 deletions
diff --git a/gatchat/Makefile.am b/gatchat/Makefile.am
index 9f1da113..8b2ed200 100644
--- a/gatchat/Makefile.am
+++ b/gatchat/Makefile.am
@@ -2,7 +2,7 @@
noinst_LTLIBRARIES = libgatchat.la
libgatchat_la_SOURCES = gatchat.h gatchat.c gatresult.h gatresult.c \
- ringbuffer.h ringbuffer.c
+ ringbuffer.h ringbuffer.c gatsyntax.h gatsyntax.c
AM_CFLAGS = @GLIB_CFLAGS@
diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c
index d06b5008..8263ad71 100644
--- a/gatchat/gatchat.c
+++ b/gatchat/gatchat.c
@@ -33,30 +33,12 @@
#include <glib.h>
#include "ringbuffer.h"
-#include "gatresult.h"
#include "gatchat.h"
/* #define WRITE_SCHEDULER_DEBUG 1 */
static void g_at_chat_wakeup_writer(GAtChat *chat);
-
-enum chat_state {
- PARSER_STATE_IDLE = 0,
- PARSER_STATE_INITIAL_CR,
- PARSER_STATE_INITIAL_LF,
- PARSER_STATE_RESPONSE,
- PARSER_STATE_TERMINATOR_CR,
- PARSER_STATE_RESPONSE_COMPLETE,
- PARSER_STATE_GUESS_MULTILINE_RESPONSE,
- PARSER_STATE_MULTILINE_RESPONSE,
- PARSER_STATE_MULTILINE_TERMINATOR_CR,
- PARSER_STATE_MULTILINE_COMPLETE,
- PARSER_STATE_PDU,
- PARSER_STATE_PDU_CR,
- PARSER_STATE_PDU_COMPLETE,
- PARSER_STATE_PROMPT,
- PARSER_STATE_PROMPT_COMPLETE
-};
+static void debug_chat(GAtChat *chat, gboolean in, const char *str, gsize len);
struct at_command {
char *cmd;
@@ -95,14 +77,13 @@ struct _GAtChat {
struct ring_buffer *buf; /* Current read buffer */
guint read_so_far; /* Number of bytes processed */
gboolean disconnecting; /* Whether we're disconnecting */
- enum chat_state state; /* Current chat state */
- int flags;
char *pdu_notify; /* Unsolicited Resp w/ PDU */
GSList *response_lines; /* char * lines of the response */
char *wakeup; /* command sent to wakeup modem */
gdouble inactivity_time; /* Period of inactivity */
guint wakeup_timeout; /* How long to wait for resp */
GTimer *wakeup_timer; /* Keep track of elapsed time */
+ GAtSyntax *syntax;
};
static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
@@ -254,6 +235,9 @@ static void g_at_chat_cleanup(GAtChat *chat)
g_timer_destroy(chat->wakeup_timer);
chat->wakeup_timer = 0;
}
+
+ g_at_syntax_unref(chat->syntax);
+ chat->syntax = NULL;
}
static void read_watcher_destroy_notify(GAtChat *chat)
@@ -306,7 +290,10 @@ static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
if (notify->pdu) {
chat->pdu_notify = line;
- chat->state = PARSER_STATE_PDU;
+
+ if (chat->syntax->set_hint)
+ chat->syntax->set_hint(chat->syntax,
+ G_AT_SYNTAX_EXPECT_PDU);
return TRUE;
}
@@ -321,7 +308,6 @@ static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
if (ret) {
g_slist_free(result.lines);
g_free(line);
- chat->state = PARSER_STATE_IDLE;
}
return ret;
@@ -387,8 +373,6 @@ static gboolean g_at_chat_handle_command_response(GAtChat *p,
int i;
int size = sizeof(terminator_table) / sizeof(struct terminator_info);
- p->state = PARSER_STATE_IDLE;
-
for (i = 0; i < size; i++) {
struct terminator_info *info = &terminator_table[i];
@@ -415,8 +399,8 @@ static gboolean g_at_chat_handle_command_response(GAtChat *p,
}
out:
- if (!(p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
- p->state = PARSER_STATE_GUESS_MULTILINE_RESPONSE;
+ if (p->syntax->set_hint)
+ p->syntax->set_hint(p->syntax, G_AT_SYNTAX_EXPECT_MULTILINE);
if (cmd->listing) {
GAtResult result;
@@ -434,31 +418,13 @@ out:
return TRUE;
}
-static void have_line(GAtChat *p, gboolean strip_preceding)
+static void have_line(GAtChat *p, char *str)
{
/* We're not going to copy terminal <CR><LF> */
- unsigned int len = p->read_so_far - 2;
- char *str;
struct at_command *cmd;
- /* If we have preceding <CR><LF> modify the len */
- if (strip_preceding)
- len -= 2;
-
- /* Make sure we have terminal null */
- str = g_try_new(char, len + 1);
-
- if (!str) {
- ring_buffer_drain(p->buf, p->read_so_far);
+ if (!str)
return;
- }
-
- if (strip_preceding)
- ring_buffer_drain(p->buf, 2);
- ring_buffer_read(p->buf, str, len);
- ring_buffer_drain(p->buf, 2);
-
- str[len] = '\0';
/* Check for echo, this should not happen, but lets be paranoid */
if (!strncmp(str, "AT", 2) == TRUE)
@@ -488,30 +454,18 @@ static void have_line(GAtChat *p, gboolean strip_preceding)
done:
/* No matches & no commands active, ignore line */
g_free(str);
- p->state = PARSER_STATE_IDLE;
}
-static void have_pdu(GAtChat *p)
+static void have_pdu(GAtChat *p, char *pdu)
{
- unsigned int len = p->read_so_far - 2;
- char *pdu;
GHashTableIter iter;
struct at_notify *notify;
char *prefix;
gpointer key, value;
GAtResult result;
- pdu = g_try_new(char, len + 1);
-
- if (!pdu) {
- ring_buffer_drain(p->buf, p->read_so_far);
+ if (!pdu)
goto out;
- }
-
- ring_buffer_read(p->buf, pdu, len);
- ring_buffer_drain(p->buf, 2);
-
- pdu[len] = '\0';
result.lines = g_slist_prepend(NULL, p->pdu_notify);
result.final_or_pdu = pdu;
@@ -540,97 +494,47 @@ out:
if (pdu)
g_free(pdu);
-
- p->state = PARSER_STATE_IDLE;
}
-static inline void parse_char(GAtChat *chat, char byte)
+static char *extract_line(GAtChat *p)
{
- switch (chat->state) {
- case PARSER_STATE_IDLE:
- if (byte == '\r')
- chat->state = PARSER_STATE_INITIAL_CR;
- else if (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) {
- if (byte == '>')
- chat->state = PARSER_STATE_PROMPT;
+ unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
+ unsigned int pos = 0;
+ unsigned char *buf = ring_buffer_read_ptr(p->buf, pos);
+ int strip_front = 0;
+ int line_length = 0;
+ char *line;
+
+ while (pos < p->read_so_far) {
+ if (*buf == '\r' || *buf == '\n')
+ if (!line_length)
+ strip_front += 1;
else
- chat->state = PARSER_STATE_RESPONSE;
- }
- break;
-
- case PARSER_STATE_INITIAL_CR:
- if (byte == '\n')
- chat->state = PARSER_STATE_INITIAL_LF;
- else if (byte != '\r' && /* Echo & no <CR><LF>?! */
- (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
- chat->state = PARSER_STATE_RESPONSE;
- else if (byte != '\r')
- chat->state = PARSER_STATE_IDLE;
- break;
-
- case PARSER_STATE_INITIAL_LF:
- if (byte == '\r')
- chat->state = PARSER_STATE_TERMINATOR_CR;
- else if (byte == '>')
- chat->state = PARSER_STATE_PROMPT;
+ break;
else
- chat->state = PARSER_STATE_RESPONSE;
- break;
+ line_length += 1;
- case PARSER_STATE_RESPONSE:
- if (byte == '\r')
- chat->state = PARSER_STATE_TERMINATOR_CR;
- break;
+ buf += 1;
+ pos += 1;
- case PARSER_STATE_TERMINATOR_CR:
- if (byte == '\n')
- chat->state = PARSER_STATE_RESPONSE_COMPLETE;
- else
- chat->state = PARSER_STATE_IDLE;
- break;
+ if (pos == wrap)
+ buf = ring_buffer_read_ptr(p->buf, pos);
+ }
- case PARSER_STATE_GUESS_MULTILINE_RESPONSE:
- if (byte == '\r')
- chat->state = PARSER_STATE_INITIAL_CR;
- else
- chat->state = PARSER_STATE_MULTILINE_RESPONSE;
- break;
-
- case PARSER_STATE_MULTILINE_RESPONSE:
- if (byte == '\r')
- chat->state = PARSER_STATE_MULTILINE_TERMINATOR_CR;
- break;
-
- case PARSER_STATE_MULTILINE_TERMINATOR_CR:
- if (byte == '\n')
- chat->state = PARSER_STATE_MULTILINE_COMPLETE;
- break;
-
- case PARSER_STATE_PDU:
- if (byte == '\r')
- chat->state = PARSER_STATE_PDU_CR;
- break;
-
- case PARSER_STATE_PDU_CR:
- if (byte == '\n')
- chat->state = PARSER_STATE_PDU_COMPLETE;
- break;
-
- case PARSER_STATE_PROMPT:
- if (byte == ' ')
- chat->state = PARSER_STATE_PROMPT_COMPLETE;
- else
- chat->state = PARSER_STATE_RESPONSE;
- break;
-
- case PARSER_STATE_RESPONSE_COMPLETE:
- case PARSER_STATE_PDU_COMPLETE:
- case PARSER_STATE_MULTILINE_COMPLETE:
- default:
- /* This really shouldn't happen */
- assert(FALSE);
- return;
+ line = g_try_new(char, line_length + 1);
+
+ if (!line) {
+ ring_buffer_drain(p->buf, p->read_so_far);
+ return NULL;
}
+
+ ring_buffer_drain(p->buf, strip_front);
+ ring_buffer_read(p->buf, line, line_length);
+ ring_buffer_drain(p->buf, p->read_so_far - strip_front - line_length);
+
+ line[line_length] = '\0';
+
+ return line;
}
static void new_bytes(GAtChat *p)
@@ -639,76 +543,45 @@ static void new_bytes(GAtChat *p)
unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
+ GAtSyntaxResult result;
+
while (p->read_so_far < len) {
- parse_char(p, *buf);
+ gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
+ result = p->syntax->feed(p->syntax, (char *)buf, &rbytes);
- buf += 1;
- p->read_so_far += 1;
+ buf += rbytes;
+ p->read_so_far += rbytes;
if (p->read_so_far == wrap) {
buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
wrap = len;
}
- if (p->state == PARSER_STATE_RESPONSE_COMPLETE) {
- gboolean strip_preceding;
-
- if (p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF)
- strip_preceding = FALSE;
- else
- strip_preceding = TRUE;
-
- len -= p->read_so_far;
- wrap -= p->read_so_far;
-
- have_line(p, strip_preceding);
-
- p->read_so_far = 0;
- } else if (p->state == PARSER_STATE_MULTILINE_COMPLETE) {
- len -= p->read_so_far;
- wrap -= p->read_so_far;
-
- have_line(p, FALSE);
-
- p->read_so_far = 0;
- } else if (p->state == PARSER_STATE_PDU_COMPLETE) {
- len -= p->read_so_far;
- wrap -= p->read_so_far;
-
- /* Some modems like the TI Calypso send a CMT style
- * notification with an extra CRLF thrown in
- */
- if ((p->flags & G_AT_CHAT_FLAG_EXTRA_PDU_CRLF) &&
- p->read_so_far == 2) {
- p->state = PARSER_STATE_PDU;
- ring_buffer_drain(p->buf, p->read_so_far);
- } else
- have_pdu(p);
-
- p->read_so_far = 0;
- } else if (p->state == PARSER_STATE_INITIAL_CR) {
- len -= p->read_so_far - 1;
- wrap -= p->read_so_far - 1;
+ if (result == G_AT_SYNTAX_RESULT_UNSURE)
+ continue;
- ring_buffer_drain(p->buf, p->read_so_far - 1);
+ switch (result) {
+ case G_AT_SYNTAX_RESULT_LINE:
+ case G_AT_SYNTAX_RESULT_MULTILINE:
+ have_line(p, extract_line(p));
+ break;
- p->read_so_far = 1;
- } else if (p->state == PARSER_STATE_PROMPT_COMPLETE) {
- len -= p->read_so_far;
- wrap -= p->read_so_far;
+ case G_AT_SYNTAX_RESULT_PDU:
+ have_pdu(p, extract_line(p));
+ break;
+ case G_AT_SYNTAX_RESULT_PROMPT:
g_at_chat_wakeup_writer(p);
-
ring_buffer_drain(p->buf, p->read_so_far);
+ break;
- p->read_so_far = 0;
-
- p->state = PARSER_STATE_IDLE;
+ default:
+ ring_buffer_drain(p->buf, p->read_so_far);
+ break;
}
- }
- if (p->state == PARSER_STATE_IDLE && p->read_so_far > 0) {
- ring_buffer_drain(p->buf, p->read_so_far);
+ len -= p->read_so_far;
+ wrap -= p->read_so_far;
p->read_so_far = 0;
}
}
@@ -736,13 +609,8 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
* this cannot happen under normal circumstances, so probably
* the channel is getting garbage, drop off
*/
- if (toread == 0) {
- if (chat->state == PARSER_STATE_RESPONSE)
- return FALSE;
-
- err = G_IO_ERROR_AGAIN;
- break;
- }
+ if (toread == 0)
+ return FALSE;
buf = ring_buffer_write_ptr(chat->buf);
@@ -900,7 +768,7 @@ static void g_at_chat_wakeup_writer(GAtChat *chat)
(GDestroyNotify)write_watcher_destroy_notify);
}
-GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
+GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax)
{
GAtChat *chat;
GIOFlags io_flags;
@@ -908,6 +776,9 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
if (!channel)
return NULL;
+ if (!syntax)
+ return NULL;
+
chat = g_try_new0(GAtChat, 1);
if (!chat)
@@ -916,7 +787,6 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
chat->ref_count = 1;
chat->next_cmd_id = 1;
chat->next_notify_id = 1;
- chat->flags = flags;
chat->buf = ring_buffer_new(4096);
@@ -951,6 +821,8 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
received_data, chat,
(GDestroyNotify)read_watcher_destroy_notify);
+ chat->syntax = g_at_syntax_ref(syntax);
+
return chat;
error:
@@ -967,22 +839,6 @@ error:
return NULL;
}
-int g_at_chat_get_flags(GAtChat *chat)
-{
- if (chat == NULL)
- return 0;
-
- return chat->flags;
-}
-
-void g_at_chat_set_flags(GAtChat *chat, int flags)
-{
- if (chat == NULL)
- return;
-
- chat->flags = flags;
-}
-
static int open_device(const char *device)
{
struct termios ti;
@@ -1003,7 +859,7 @@ static int open_device(const char *device)
return fd;
}
-GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
+GAtChat *g_at_chat_new_from_tty(const char *device, GAtSyntax *syntax)
{
GIOChannel *channel;
int fd;
@@ -1018,7 +874,7 @@ GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
return NULL;
}
- return g_at_chat_new(channel, flags);
+ return g_at_chat_new(channel, syntax);
}
GAtChat *g_at_chat_ref(GAtChat *chat)
diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h
index 969f6f46..d0a59b83 100644
--- a/gatchat/gatchat.h
+++ b/gatchat/gatchat.h
@@ -27,6 +27,7 @@ extern "C" {
#endif
#include "gatresult.h"
+#include "gatsyntax.h"
struct _GAtChat;
@@ -44,11 +45,8 @@ enum _GAtChatFlags {
typedef enum _GAtChatFlags GAtChatFlags;
-GAtChat *g_at_chat_new(GIOChannel *channel, int flags);
-GAtChat *g_at_chat_new_from_tty(const char *device, int flags);
-
-int g_at_chat_get_flags(GAtChat *chat);
-void g_at_chat_set_flags(GAtChat *chat, int flags);
+GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax);
+GAtChat *g_at_chat_new_from_tty(const char *device, GAtSyntax *syntax);
GAtChat *g_at_chat_ref(GAtChat *chat);
void g_at_chat_unref(GAtChat *chat);
diff --git a/gatchat/gatsyntax.c b/gatchat/gatsyntax.c
new file mode 100644
index 00000000..7c5aadce
--- /dev/null
+++ b/gatchat/gatsyntax.c
@@ -0,0 +1,228 @@
+/*
+ *
+ * AT chat library with GLib integration
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "gatsyntax.h"
+
+enum GSMV1_STATE_ {
+ GSMV1_STATE_IDLE = 0,
+ GSMV1_STATE_INITIAL_CR,
+ GSMV1_STATE_INITIAL_LF,
+ GSMV1_STATE_RESPONSE,
+ GSMV1_STATE_TERMINATOR_CR,
+ GSMV1_STATE_GUESS_MULTILINE_RESPONSE,
+ GSMV1_STATE_MULTILINE_RESPONSE,
+ GSMV1_STATE_MULTILINE_TERMINATOR_CR,
+ GSMV1_STATE_PDU,
+ GSMV1_STATE_PDU_CR,
+ GSMV1_STATE_PROMPT,
+ GSMV1_STATE_GARBAGE,
+ GSMV1_STATE_GARBAGE_CHECK_LF,
+};
+
+static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
+{
+ switch (hint) {
+ case G_AT_SYNTAX_EXPECT_PDU:
+ syntax->state = GSMV1_STATE_PDU;
+ break;
+ case G_AT_SYNTAX_EXPECT_MULTILINE:
+ syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE;
+ break;
+ default:
+ break;
+ };
+}
+
+static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax,
+ const char *bytes, gsize *len)
+{
+ gsize i = 0;
+ GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
+
+ while (i < *len) {
+ char byte = bytes[i];
+
+ switch (syntax->state) {
+ case GSMV1_STATE_IDLE:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_INITIAL_CR;
+ else
+ syntax->state = GSMV1_STATE_GARBAGE;
+ break;
+
+ case GSMV1_STATE_INITIAL_CR:
+ if (byte == '\n')
+ syntax->state = GSMV1_STATE_INITIAL_LF;
+ else
+ syntax->state = GSMV1_STATE_GARBAGE;
+ break;
+
+ case GSMV1_STATE_INITIAL_LF:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_TERMINATOR_CR;
+ else if (byte == '>')
+ syntax->state = GSMV1_STATE_PROMPT;
+ else
+ syntax->state = GSMV1_STATE_RESPONSE;
+ break;
+
+ case GSMV1_STATE_RESPONSE:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_TERMINATOR_CR;
+ break;
+
+ case GSMV1_STATE_TERMINATOR_CR:
+ syntax->state = GSMV1_STATE_IDLE;
+
+ if (byte == '\n') {
+ i += 1;
+ res = G_AT_SYNTAX_RESULT_LINE;
+ } else
+ res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+ goto out;
+
+ case GSMV1_STATE_GUESS_MULTILINE_RESPONSE:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_INITIAL_CR;
+ else
+ syntax->state = GSMV1_STATE_MULTILINE_RESPONSE;
+ break;
+
+ case GSMV1_STATE_MULTILINE_RESPONSE:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR;
+ break;
+
+ case GSMV1_STATE_MULTILINE_TERMINATOR_CR:
+ syntax->state = GSMV1_STATE_IDLE;
+
+ if (byte == '\n') {
+ i += 1;
+ res = G_AT_SYNTAX_RESULT_MULTILINE;
+ } else
+ res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+ goto out;
+
+ case GSMV1_STATE_PDU:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_PDU_CR;
+ break;
+
+ case GSMV1_STATE_PDU_CR:
+ syntax->state = GSMV1_STATE_IDLE;
+
+ if (byte == '\n') {
+ i += 1;
+ res = G_AT_SYNTAX_RESULT_PDU;
+ } else
+ res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+ goto out;
+
+ case GSMV1_STATE_PROMPT:
+ if (byte == ' ') {
+ syntax->state = GSMV1_STATE_IDLE;
+ i += 1;
+ res = G_AT_SYNTAX_RESULT_PROMPT;
+ goto out;
+ }
+
+ syntax->state = GSMV1_STATE_RESPONSE;
+ return G_AT_SYNTAX_RESULT_UNSURE;
+
+ case GSMV1_STATE_GARBAGE:
+ if (byte == '\r')
+ syntax->state = GSMV1_STATE_GARBAGE_CHECK_LF;
+ break;
+
+ case GSMV1_STATE_GARBAGE_CHECK_LF:
+ syntax->state = GSMV1_STATE_IDLE;
+ res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+
+ if (byte == '\n')
+ i += 1;
+
+ goto out;
+
+ default:
+ break;
+ };
+
+ i += 1;
+ }
+
+out:
+ *len = i;
+ return res;
+}
+
+GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
+ GAtSyntaxSetHintFunc hint,
+ int initial_state)
+{
+ GAtSyntax *syntax;
+
+ syntax = g_new0(GAtSyntax, 1);
+
+ syntax->feed = feed;
+ syntax->set_hint = hint;
+ syntax->state = initial_state;
+ syntax->ref_count = 1;
+
+ return syntax;
+}
+
+
+GAtSyntax *g_at_syntax_new_gsmv1()
+{
+ return g_at_syntax_new_full(gsmv1_feed, gsmv1_hint, GSMV1_STATE_IDLE);
+}
+
+GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax)
+{
+ if (syntax == NULL)
+ return NULL;
+
+ g_atomic_int_inc(&syntax->ref_count);
+
+ return syntax;
+}
+
+void g_at_syntax_unref(GAtSyntax *syntax)
+{
+ gboolean is_zero;
+
+ if (syntax == NULL)
+ return;
+
+ is_zero = g_atomic_int_dec_and_test(&syntax->ref_count);
+
+ if (is_zero)
+ g_free(syntax);
+}
diff --git a/gatchat/gatsyntax.h b/gatchat/gatsyntax.h
new file mode 100644
index 00000000..57edeade
--- /dev/null
+++ b/gatchat/gatsyntax.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * AT chat library with GLib integration
+ *
+ * Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __GATSYNTAX_H
+#define __GATSYNTAX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum _GAtSyntaxExpectHint {
+ G_AT_SYNTAX_EXPECT_PDU,
+ G_AT_SYNTAX_EXPECT_MULTILINE,
+ G_AT_SYNTAX_EXPECT_PROMPT
+};
+
+typedef enum _GAtSyntaxExpectHint GAtSyntaxExpectHint;
+
+enum _GAtSyntaxResult {
+ G_AT_SYNTAX_RESULT_UNRECOGNIZED,
+ G_AT_SYNTAX_RESULT_UNSURE,
+ G_AT_SYNTAX_RESULT_LINE,
+ G_AT_SYNTAX_RESULT_MULTILINE,
+ G_AT_SYNTAX_RESULT_PDU,
+ G_AT_SYNTAX_RESULT_PROMPT,
+};
+
+typedef enum _GAtSyntaxResult GAtSyntaxResult;
+
+typedef struct _GAtSyntax GAtSyntax;
+
+typedef void (*GAtSyntaxSetHintFunc)(GAtSyntax *syntax,
+ GAtSyntaxExpectHint hint);
+typedef GAtSyntaxResult (*GAtSyntaxFeedFunc)(GAtSyntax *syntax,
+ const char *bytes, gsize *len);
+
+struct _GAtSyntax {
+ gint ref_count;
+ int state;
+ GAtSyntaxSetHintFunc set_hint;
+ GAtSyntaxFeedFunc feed;
+};
+
+
+GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
+ GAtSyntaxSetHintFunc hint,
+ int initial_state);
+GAtSyntax *g_at_syntax_new_gsmv1();
+
+GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax);
+void g_at_syntax_unref(GAtSyntax *syntax);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GATSYNTAX_H */