From 0287b33447a2880e8e3170522f42d90ea3038770 Mon Sep 17 00:00:00 2001 From: Denis Kenzior 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 #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 */ unsigned int len = p->read_so_far - 2; - char *str; struct at_command *cmd; - /* If we have preceding 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 ?! */ - (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 +#endif + +#include + +#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