From 473c3c902bd74a59c78b4053eaf3202089ae5b1f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 6 Aug 2009 17:53:30 -0700 Subject: [PATCH] Sync up with oFono's GAtChat version --- gatchat/gatchat.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gatchat/gatchat.h | 15 +++++---- gatchat/gatsyntax.c | 25 +++++++++++++- 3 files changed, 125 insertions(+), 8 deletions(-) diff --git a/gatchat/gatchat.c b/gatchat/gatchat.c index 6cb5207..1347f18 100644 --- a/gatchat/gatchat.c +++ b/gatchat/gatchat.c @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -77,6 +78,8 @@ struct _GAtChat { struct ring_buffer *buf; /* Current read buffer */ guint read_so_far; /* Number of bytes processed */ gboolean disconnecting; /* Whether we're disconnecting */ + GAtDebugFunc debugf; /* debugging output function */ + gpointer debug_data; /* Data to pass to debug func */ char *pdu_notify; /* Unsolicited Resp w/ PDU */ GSList *response_lines; /* char * lines of the response */ char *wakeup; /* command sent to wakeup modem */ @@ -590,6 +593,81 @@ static void new_bytes(GAtChat *p) g_at_chat_shutdown(p); } +static void debug_chat(GAtChat *chat, gboolean in, const char *str, gsize len) +{ + char type = in ? '<' : '>'; + gsize escaped = 2; /* Enough for '<', ' ' */ + char *escaped_str; + const char *esc = ""; + gsize esc_size = strlen(esc); + const char *ctrlz = ""; + gsize ctrlz_size = strlen(ctrlz); + gsize i; + + if (!chat->debugf || !len) + return; + + for (i = 0; i < len; i++) { + char c = str[i]; + + if (isprint(c)) + escaped += 1; + else if (c == '\r' || c == '\t' || c == '\n') + escaped += 2; + else if (c == 26) + escaped += ctrlz_size; + else if (c == 25) + escaped += esc_size; + else + escaped += 4; + } + + escaped_str = g_malloc(escaped + 1); + escaped_str[0] = type; + escaped_str[1] = ' '; + escaped_str[2] = '\0'; + escaped_str[escaped] = '\0'; + + for (escaped = 2, i = 0; i < len; i++) { + char c = str[i]; + + switch (c) { + case '\r': + escaped_str[escaped++] = '\\'; + escaped_str[escaped++] = 'r'; + break; + case '\t': + escaped_str[escaped++] = '\\'; + escaped_str[escaped++] = 't'; + break; + case '\n': + escaped_str[escaped++] = '\\'; + escaped_str[escaped++] = 'n'; + break; + case 26: + strncpy(&escaped_str[escaped], ctrlz, ctrlz_size); + escaped += ctrlz_size; + break; + case 25: + strncpy(&escaped_str[escaped], esc, esc_size); + escaped += esc_size; + break; + default: + if (isprint(c)) + escaped_str[escaped++] = c; + else { + escaped_str[escaped++] = '\\'; + escaped_str[escaped++] = '0' + ((c >> 6) & 07); + escaped_str[escaped++] = '0' + ((c >> 3) & 07); + escaped_str[escaped++] = '0' + (c & 07); + } + } + } + + chat->debugf(escaped_str, chat->debug_data); + g_free(escaped_str); +} + static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer data) { @@ -615,6 +693,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, buf = ring_buffer_write_ptr(chat->buf); err = g_io_channel_read(channel, (char *) buf, toread, &rbytes); + debug_chat(chat, TRUE, (char *)buf, rbytes); total_read += rbytes; @@ -741,6 +820,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, return FALSE; } + debug_chat(chat, FALSE, cmd->cmd + chat->cmd_bytes_written, + bytes_written); chat->cmd_bytes_written += bytes_written; if (bytes_written < towrite) @@ -784,6 +865,7 @@ GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax) chat->ref_count = 1; chat->next_cmd_id = 1; chat->next_notify_id = 1; + chat->debugf = NULL; chat->buf = ring_buffer_new(4096); @@ -929,6 +1011,17 @@ gboolean g_at_chat_set_disconnect_function(GAtChat *chat, return TRUE; } +gboolean g_at_chat_set_debug(GAtChat *chat, GAtDebugFunc func, gpointer user) +{ + if (chat == NULL) + return FALSE; + + chat->debugf = func; + chat->debug_data = user; + + return TRUE; +} + static guint send_common(GAtChat *chat, const char *cmd, const char **prefix_list, GAtNotifyFunc listing, GAtResultFunc func, diff --git a/gatchat/gatchat.h b/gatchat/gatchat.h index d0a59b8..97cf461 100644 --- a/gatchat/gatchat.h +++ b/gatchat/gatchat.h @@ -37,13 +37,7 @@ typedef void (*GAtResultFunc)(gboolean success, GAtResult *result, gpointer user_data); typedef void (*GAtNotifyFunc)(GAtResult *result, gpointer user_data); typedef void (*GAtDisconnectFunc)(gpointer user_data); - -enum _GAtChatFlags { - G_AT_CHAT_FLAG_NO_LEADING_CRLF = 1, /* Some emulators are broken */ - G_AT_CHAT_FLAG_EXTRA_PDU_CRLF = 2, -}; - -typedef enum _GAtChatFlags GAtChatFlags; +typedef void (*GAtDebugFunc)(const char *str, gpointer user_data); GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax); GAtChat *g_at_chat_new_from_tty(const char *device, GAtSyntax *syntax); @@ -57,6 +51,13 @@ gboolean g_at_chat_set_disconnect_function(GAtChat *chat, GAtDisconnectFunc disconnect, gpointer user_data); /*! + * If the function is not NULL, then on every read/write from the GIOChannel + * provided to GAtChat the logging function will be called with the + * input/output string and user data + */ +gboolean g_at_chat_set_debug(GAtChat *chat, GAtDebugFunc func, gpointer user); + +/*! * Queue an AT command for execution. The command contents are given * in cmd. Once the command executes, the callback function given by * func is called with user provided data in user_data. diff --git a/gatchat/gatsyntax.c b/gatchat/gatsyntax.c index 7c5aadc..5db01a7 100644 --- a/gatchat/gatsyntax.c +++ b/gatchat/gatsyntax.c @@ -36,6 +36,8 @@ enum GSMV1_STATE_ { GSMV1_STATE_GUESS_MULTILINE_RESPONSE, GSMV1_STATE_MULTILINE_RESPONSE, GSMV1_STATE_MULTILINE_TERMINATOR_CR, + GSMV1_STATE_PDU_CHECK_EXTRA_CR, + GSMV1_STATE_PDU_CHECK_EXTRA_LF, GSMV1_STATE_PDU, GSMV1_STATE_PDU_CR, GSMV1_STATE_PROMPT, @@ -47,7 +49,7 @@ static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint) { switch (hint) { case G_AT_SYNTAX_EXPECT_PDU: - syntax->state = GSMV1_STATE_PDU; + syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_CR; break; case G_AT_SYNTAX_EXPECT_MULTILINE: syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE; @@ -129,6 +131,27 @@ static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax, goto out; + /* Some 27.007 compliant modems still get this wrong. They + * insert an extra CRLF between the command and he PDU, + * in effect making them two separate lines. We try to + * handle this case gracefully + */ + case GSMV1_STATE_PDU_CHECK_EXTRA_CR: + if (byte == '\r') + syntax->state = GSMV1_STATE_PDU_CHECK_EXTRA_LF; + else + syntax->state = GSMV1_STATE_PDU; + break; + + case GSMV1_STATE_PDU_CHECK_EXTRA_LF: + res = G_AT_SYNTAX_RESULT_UNRECOGNIZED; + syntax->state = GSMV1_STATE_PDU; + + if (byte == '\n') + i += 1; + + goto out; + case GSMV1_STATE_PDU: if (byte == '\r') syntax->state = GSMV1_STATE_PDU_CR; -- 2.7.4