Sync up with oFono's GAtChat version
authorMarcel Holtmann <marcel@holtmann.org>
Fri, 7 Aug 2009 00:53:30 +0000 (17:53 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 7 Aug 2009 00:53:30 +0000 (17:53 -0700)
gatchat/gatchat.c
gatchat/gatchat.h
gatchat/gatsyntax.c

index 6cb5207..1347f18 100644 (file)
@@ -29,6 +29,7 @@
 #include <string.h>
 #include <assert.h>
 #include <termios.h>
+#include <ctype.h>
 
 #include <glib.h>
 
@@ -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 = "<ESC>";
+       gsize esc_size = strlen(esc);
+       const char *ctrlz = "<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,
index d0a59b8..97cf461 100644 (file)
@@ -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.
index 7c5aadc..5db01a7 100644 (file)
@@ -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;