Refactor GAtChat to accept user-provided parsers

Intended for really broken modems.  A default 27.007 compliant
parser is provided.
This commit is contained in:
Denis Kenzior 2009-08-04 18:53:25 -05:00
parent 239ab461f7
commit 83820c88b2
5 changed files with 389 additions and 232 deletions

View File

@ -2,7 +2,7 @@
noinst_LTLIBRARIES = libgatchat.la noinst_LTLIBRARIES = libgatchat.la
libgatchat_la_SOURCES = gatchat.h gatchat.c gatresult.h gatresult.c \ 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@ AM_CFLAGS = @GLIB_CFLAGS@

View File

@ -33,30 +33,12 @@
#include <glib.h> #include <glib.h>
#include "ringbuffer.h" #include "ringbuffer.h"
#include "gatresult.h"
#include "gatchat.h" #include "gatchat.h"
/* #define WRITE_SCHEDULER_DEBUG 1 */ /* #define WRITE_SCHEDULER_DEBUG 1 */
static void g_at_chat_wakeup_writer(GAtChat *chat); static void g_at_chat_wakeup_writer(GAtChat *chat);
static void debug_chat(GAtChat *chat, gboolean in, const char *str, gsize len);
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 { struct at_command {
char *cmd; char *cmd;
@ -95,14 +77,13 @@ struct _GAtChat {
struct ring_buffer *buf; /* Current read buffer */ struct ring_buffer *buf; /* Current read buffer */
guint read_so_far; /* Number of bytes processed */ guint read_so_far; /* Number of bytes processed */
gboolean disconnecting; /* Whether we're disconnecting */ gboolean disconnecting; /* Whether we're disconnecting */
enum chat_state state; /* Current chat state */
int flags;
char *pdu_notify; /* Unsolicited Resp w/ PDU */ char *pdu_notify; /* Unsolicited Resp w/ PDU */
GSList *response_lines; /* char * lines of the response */ GSList *response_lines; /* char * lines of the response */
char *wakeup; /* command sent to wakeup modem */ char *wakeup; /* command sent to wakeup modem */
gdouble inactivity_time; /* Period of inactivity */ gdouble inactivity_time; /* Period of inactivity */
guint wakeup_timeout; /* How long to wait for resp */ guint wakeup_timeout; /* How long to wait for resp */
GTimer *wakeup_timer; /* Keep track of elapsed time */ GTimer *wakeup_timer; /* Keep track of elapsed time */
GAtSyntax *syntax;
}; };
static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b) 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); g_timer_destroy(chat->wakeup_timer);
chat->wakeup_timer = 0; chat->wakeup_timer = 0;
} }
g_at_syntax_unref(chat->syntax);
chat->syntax = NULL;
} }
static void read_watcher_destroy_notify(GAtChat *chat) 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) { if (notify->pdu) {
chat->pdu_notify = line; 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; return TRUE;
} }
@ -321,7 +308,6 @@ static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
if (ret) { if (ret) {
g_slist_free(result.lines); g_slist_free(result.lines);
g_free(line); g_free(line);
chat->state = PARSER_STATE_IDLE;
} }
return ret; return ret;
@ -387,8 +373,6 @@ static gboolean g_at_chat_handle_command_response(GAtChat *p,
int i; int i;
int size = sizeof(terminator_table) / sizeof(struct terminator_info); int size = sizeof(terminator_table) / sizeof(struct terminator_info);
p->state = PARSER_STATE_IDLE;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
struct terminator_info *info = &terminator_table[i]; struct terminator_info *info = &terminator_table[i];
@ -415,8 +399,8 @@ static gboolean g_at_chat_handle_command_response(GAtChat *p,
} }
out: out:
if (!(p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF)) if (p->syntax->set_hint)
p->state = PARSER_STATE_GUESS_MULTILINE_RESPONSE; p->syntax->set_hint(p->syntax, G_AT_SYNTAX_EXPECT_MULTILINE);
if (cmd->listing) { if (cmd->listing) {
GAtResult result; GAtResult result;
@ -434,31 +418,13 @@ out:
return TRUE; 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> */ /* We're not going to copy terminal <CR><LF> */
unsigned int len = p->read_so_far - 2;
char *str;
struct at_command *cmd; struct at_command *cmd;
/* If we have preceding <CR><LF> modify the len */ if (!str)
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);
return; 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 */ /* Check for echo, this should not happen, but lets be paranoid */
if (!strncmp(str, "AT", 2) == TRUE) if (!strncmp(str, "AT", 2) == TRUE)
@ -488,30 +454,18 @@ static void have_line(GAtChat *p, gboolean strip_preceding)
done: done:
/* No matches & no commands active, ignore line */ /* No matches & no commands active, ignore line */
g_free(str); 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; GHashTableIter iter;
struct at_notify *notify; struct at_notify *notify;
char *prefix; char *prefix;
gpointer key, value; gpointer key, value;
GAtResult result; GAtResult result;
pdu = g_try_new(char, len + 1); if (!pdu)
if (!pdu) {
ring_buffer_drain(p->buf, p->read_so_far);
goto out; 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.lines = g_slist_prepend(NULL, p->pdu_notify);
result.final_or_pdu = pdu; result.final_or_pdu = pdu;
@ -540,97 +494,47 @@ out:
if (pdu) if (pdu)
g_free(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) { unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
case PARSER_STATE_IDLE: unsigned int pos = 0;
if (byte == '\r') unsigned char *buf = ring_buffer_read_ptr(p->buf, pos);
chat->state = PARSER_STATE_INITIAL_CR; int strip_front = 0;
else if (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) { int line_length = 0;
if (byte == '>') char *line;
chat->state = PARSER_STATE_PROMPT;
while (pos < p->read_so_far) {
if (*buf == '\r' || *buf == '\n')
if (!line_length)
strip_front += 1;
else else
chat->state = PARSER_STATE_RESPONSE; break;
}
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;
else else
chat->state = PARSER_STATE_RESPONSE; line_length += 1;
break;
case PARSER_STATE_RESPONSE: buf += 1;
if (byte == '\r') pos += 1;
chat->state = PARSER_STATE_TERMINATOR_CR;
break;
case PARSER_STATE_TERMINATOR_CR: if (pos == wrap)
if (byte == '\n') buf = ring_buffer_read_ptr(p->buf, pos);
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;
} }
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) 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 int wrap = ring_buffer_len_no_wrap(p->buf);
unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far); unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
while (p->read_so_far < len) { GAtSyntaxResult result;
parse_char(p, *buf);
buf += 1; while (p->read_so_far < len) {
p->read_so_far += 1; 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) { if (p->read_so_far == wrap) {
buf = ring_buffer_read_ptr(p->buf, p->read_so_far); buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
wrap = len; wrap = len;
} }
if (p->state == PARSER_STATE_RESPONSE_COMPLETE) { if (result == G_AT_SYNTAX_RESULT_UNSURE)
gboolean strip_preceding; continue;
if (p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) switch (result) {
strip_preceding = FALSE; case G_AT_SYNTAX_RESULT_LINE:
else case G_AT_SYNTAX_RESULT_MULTILINE:
strip_preceding = TRUE; have_line(p, extract_line(p));
break;
len -= p->read_so_far; case G_AT_SYNTAX_RESULT_PDU:
wrap -= p->read_so_far; have_pdu(p, extract_line(p));
break;
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_PROMPT:
g_at_chat_wakeup_writer(p); g_at_chat_wakeup_writer(p);
ring_buffer_drain(p->buf, p->read_so_far); ring_buffer_drain(p->buf, p->read_so_far);
break;
p->read_so_far = 0; default:
ring_buffer_drain(p->buf, p->read_so_far);
p->state = PARSER_STATE_IDLE; break;
} }
}
if (p->state == PARSER_STATE_IDLE && p->read_so_far > 0) { len -= p->read_so_far;
ring_buffer_drain(p->buf, p->read_so_far); wrap -= p->read_so_far;
p->read_so_far = 0; 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 * this cannot happen under normal circumstances, so probably
* the channel is getting garbage, drop off * the channel is getting garbage, drop off
*/ */
if (toread == 0) { if (toread == 0)
if (chat->state == PARSER_STATE_RESPONSE) return FALSE;
return FALSE;
err = G_IO_ERROR_AGAIN;
break;
}
buf = ring_buffer_write_ptr(chat->buf); 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); (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; GAtChat *chat;
GIOFlags io_flags; GIOFlags io_flags;
@ -908,6 +776,9 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
if (!channel) if (!channel)
return NULL; return NULL;
if (!syntax)
return NULL;
chat = g_try_new0(GAtChat, 1); chat = g_try_new0(GAtChat, 1);
if (!chat) if (!chat)
@ -916,7 +787,6 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
chat->ref_count = 1; chat->ref_count = 1;
chat->next_cmd_id = 1; chat->next_cmd_id = 1;
chat->next_notify_id = 1; chat->next_notify_id = 1;
chat->flags = flags;
chat->buf = ring_buffer_new(4096); chat->buf = ring_buffer_new(4096);
@ -951,6 +821,8 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
received_data, chat, received_data, chat,
(GDestroyNotify)read_watcher_destroy_notify); (GDestroyNotify)read_watcher_destroy_notify);
chat->syntax = g_at_syntax_ref(syntax);
return chat; return chat;
error: error:
@ -967,22 +839,6 @@ error:
return NULL; 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) static int open_device(const char *device)
{ {
struct termios ti; struct termios ti;
@ -1003,7 +859,7 @@ static int open_device(const char *device)
return fd; 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; GIOChannel *channel;
int fd; int fd;
@ -1018,7 +874,7 @@ GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
return NULL; return NULL;
} }
return g_at_chat_new(channel, flags); return g_at_chat_new(channel, syntax);
} }
GAtChat *g_at_chat_ref(GAtChat *chat) GAtChat *g_at_chat_ref(GAtChat *chat)

View File

@ -27,6 +27,7 @@ extern "C" {
#endif #endif
#include "gatresult.h" #include "gatresult.h"
#include "gatsyntax.h"
struct _GAtChat; struct _GAtChat;
@ -44,11 +45,8 @@ enum _GAtChatFlags {
typedef enum _GAtChatFlags GAtChatFlags; typedef enum _GAtChatFlags GAtChatFlags;
GAtChat *g_at_chat_new(GIOChannel *channel, int flags); GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax);
GAtChat *g_at_chat_new_from_tty(const char *device, int flags); GAtChat *g_at_chat_new_from_tty(const char *device, GAtSyntax *syntax);
int g_at_chat_get_flags(GAtChat *chat);
void g_at_chat_set_flags(GAtChat *chat, int flags);
GAtChat *g_at_chat_ref(GAtChat *chat); GAtChat *g_at_chat_ref(GAtChat *chat);
void g_at_chat_unref(GAtChat *chat); void g_at_chat_unref(GAtChat *chat);

228
gatchat/gatsyntax.c Normal file
View File

@ -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);
}

75
gatchat/gatsyntax.h Normal file
View File

@ -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 */