All pastes #1518539 Raw Edit

Stuff

public text v1 · immutable
#1518539 ·published 2009-08-04 23:55 UTC
rendered paste body
From 0287b33447a2880e8e3170522f42d90ea3038770 Mon Sep 17 00:00:00 2001
From: Denis Kenzior <denkenz@gmail.com>
Date: Tue, 4 Aug 2009 18:53:25 -0500
Subject: [PATCH] Refactor GAtChat to accept user-provided parsers

Intended for really broken modems.  A default 27.007 compliant
parser is provided.
---
 gatchat/Makefile.am |    2 +-
 gatchat/gatchat.c   |  277 +++++++++++++--------------------------------------
 gatchat/gatchat.h   |    8 +-
 gatchat/gatsyntax.c |  197 ++++++++++++++++++++++++++++++++++++
 gatchat/gatsyntax.h |   74 ++++++++++++++
 5 files changed, 343 insertions(+), 215 deletions(-)
 create mode 100644 gatchat/gatsyntax.c
 create mode 100644 gatchat/gatsyntax.h

diff --git a/gatchat/Makefile.am b/gatchat/Makefile.am
index 9f1da11..8b2ed20 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 15cc934..ce2524c 100644
--- a/gatchat/gatchat.c
+++ b/gatchat/gatchat.c
@@ -33,31 +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
-};
-
 struct at_command {
 	char *cmd;
 	char **prefixes;
@@ -95,14 +76,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 +234,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 +289,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 +307,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 +372,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 +398,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 +417,14 @@ 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,19 @@ 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 +495,48 @@ 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 len = ring_buffer_len(p->buf);
+	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);
+	line = g_try_new(char, line_length + 1);
+
+	if (!line) {
+		ring_buffer_drain(p->buf, p->read_so_far);
 		return;
 	}
+
+	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,8 +545,10 @@ 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);
+		result = p->syntax->feed(p->syntax, *buf);
 
 		buf += 1;
 		p->read_so_far += 1;
@@ -650,50 +558,22 @@ static void new_bytes(GAtChat *p)
 			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) {
+		if (result == G_AT_SYNTAX_RESULT_LINE ||
+				result == G_AT_SYNTAX_RESULT_MULTILINE) {
 			len -= p->read_so_far;
 			wrap -= p->read_so_far;
 
-			have_line(p, FALSE);
+			have_line(p, extract_line(p));
 
 			p->read_so_far = 0;
-		} else if (p->state == PARSER_STATE_PDU_COMPLETE) {
+		} else if (result == G_AT_SYNTAX_RESULT_PDU) {
 			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);
+			have_pdu(p, extract_line(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;
-
-			ring_buffer_drain(p->buf, p->read_so_far - 1);
-
-			p->read_so_far = 1;
-		} else if (p->state == PARSER_STATE_PROMPT_COMPLETE) {
+		} else if (result == G_AT_SYNTAX_RESULT_PROMPT) {
 			len -= p->read_so_far;
 			wrap -= p->read_so_far;
 
@@ -702,15 +582,11 @@ static void new_bytes(GAtChat *p)
 			ring_buffer_drain(p->buf, p->read_so_far);
 
 			p->read_so_far = 0;
-
-			p->state = PARSER_STATE_IDLE;
+		} else if (result == G_AT_SYNTAX_RESULT_UNRECOGNIZED) {
+			ring_buffer_drain(p->buf, p->read_so_far);
+			p->read_so_far = 0;
 		}
 	}
-
-	if (p->state == PARSER_STATE_IDLE && p->read_so_far > 0) {
-		ring_buffer_drain(p->buf, p->read_so_far);
-		p->read_so_far = 0;
-	}
 }
 
 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
@@ -736,13 +612,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 +771,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 +779,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)
@@ -915,7 +789,6 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
 
 	chat->next_cmd_id = 1;
 	chat->next_notify_id = 1;
-	chat->flags = flags;
 
 	chat->buf = ring_buffer_new(4096);
 
@@ -950,6 +823,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:
@@ -966,22 +841,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;
@@ -1002,7 +861,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;
@@ -1017,7 +876,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 969f6f4..d0a59b8 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 0000000..d39d668
--- /dev/null
+++ b/gatchat/gatsyntax.c
@@ -0,0 +1,197 @@
+/*
+ *
+ *  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,
+};
+
+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, char byte)
+{
+	switch (syntax->state) {
+	case GSMV1_STATE_IDLE:
+		if (byte == '\r') {
+			syntax->state = GSMV1_STATE_INITIAL_CR;
+			return G_AT_SYNTAX_RESULT_UNSURE;
+		}
+
+		break;
+
+	case GSMV1_STATE_INITIAL_CR:
+		if (byte == '\n') {
+			syntax->state = GSMV1_STATE_INITIAL_LF;
+			return G_AT_SYNTAX_RESULT_UNSURE;
+		}
+
+		syntax->state = GSMV1_STATE_IDLE;
+		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;
+
+		return G_AT_SYNTAX_RESULT_UNSURE;
+
+	case GSMV1_STATE_RESPONSE:
+		if (byte == '\r')
+			syntax->state = GSMV1_STATE_TERMINATOR_CR;
+
+		return G_AT_SYNTAX_RESULT_UNSURE;
+
+	case GSMV1_STATE_TERMINATOR_CR:
+		syntax->state = GSMV1_STATE_IDLE;
+		
+		if (byte == '\n')
+			return G_AT_SYNTAX_RESULT_LINE;
+
+		break;
+
+	case GSMV1_STATE_GUESS_MULTILINE_RESPONSE:
+		if (byte == '\r')
+			syntax->state = GSMV1_STATE_INITIAL_CR;
+		else
+			syntax->state = GSMV1_STATE_MULTILINE_RESPONSE;
+
+		return G_AT_SYNTAX_RESULT_UNSURE;
+
+	case GSMV1_STATE_MULTILINE_RESPONSE:
+		if (byte == '\r')
+			syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR;
+
+		return G_AT_SYNTAX_RESULT_UNSURE;
+
+	case GSMV1_STATE_MULTILINE_TERMINATOR_CR:
+		syntax->state = GSMV1_STATE_IDLE;
+
+		if (byte == '\n')
+			return G_AT_SYNTAX_RESULT_MULTILINE;
+
+		break;
+
+	case GSMV1_STATE_PDU:
+		if (byte == '\r')
+			syntax->state = GSMV1_STATE_PDU_CR;
+
+		return G_AT_SYNTAX_RESULT_UNSURE;
+
+	case GSMV1_STATE_PDU_CR:
+		syntax->state = GSMV1_STATE_IDLE;
+
+		if (byte == '\n')
+			return G_AT_SYNTAX_RESULT_PDU;
+
+		break;
+
+	case GSMV1_STATE_PROMPT:
+		if (byte == ' ') {
+			syntax->state = GSMV1_STATE_IDLE;
+			return G_AT_SYNTAX_RESULT_PROMPT;
+		}
+
+		syntax->state = GSMV1_STATE_RESPONSE;
+		return G_AT_SYNTAX_RESULT_UNSURE;
+
+	default:
+		break;
+	}
+
+	return G_AT_SYNTAX_RESULT_UNRECOGNIZED;
+}
+
+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 0000000..a930775
--- /dev/null
+++ b/gatchat/gatsyntax.h
@@ -0,0 +1,74 @@
+/*
+ *
+ *  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;
+
+struct _GAtSyntax
+{
+	gint ref_count;
+	int state;
+	void (*set_hint)(struct _GAtSyntax *syntax, GAtSyntaxExpectHint hint);
+	GAtSyntaxResult (*feed)(struct _GAtSyntax *syntax, char ch);
+};
+
+typedef struct _GAtSyntax GAtSyntax;
+
+typedef void (*GAtSyntaxSetHintFunc)(GAtSyntax *syntax,
+					GAtSyntaxExpectHint hint);
+typedef GAtSyntaxResult (*GAtSyntaxFeedFunc)(GAtSyntax *syntax, char ch);
+
+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 */
-- 
1.6.0.6