From 83820c88b2353c5df5badd7e509d43fc7ea4ae28 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 | 308 ++++++++++++-------------------------------- gatchat/gatchat.h | 8 +- gatchat/gatsyntax.c | 228 ++++++++++++++++++++++++++++++++ gatchat/gatsyntax.h | 75 +++++++++++ 5 files changed, 389 insertions(+), 232 deletions(-) create mode 100644 gatchat/gatsyntax.c create mode 100644 gatchat/gatsyntax.h 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 #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 */ - 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,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 ?! */ - (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; - - 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; + if (pos == wrap) + buf = ring_buffer_read_ptr(p->buf, pos); } + + 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); - while (p->read_so_far < len) { - parse_char(p, *buf); + GAtSyntaxResult result; - buf += 1; - p->read_so_far += 1; + while (p->read_so_far < len) { + gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far); + result = p->syntax->feed(p->syntax, (char *)buf, &rbytes); + + 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 (result == G_AT_SYNTAX_RESULT_UNSURE) + continue; - if (p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) - strip_preceding = FALSE; - else - strip_preceding = TRUE; + switch (result) { + case G_AT_SYNTAX_RESULT_LINE: + case G_AT_SYNTAX_RESULT_MULTILINE: + have_line(p, extract_line(p)); + break; - 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; - - ring_buffer_drain(p->buf, p->read_so_far - 1); - - 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 +#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, + 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 */