[PATCH 3/4] Add AT command parsing support and interface
Zhenhua Zhang
zhenhua.zhang at intel.com
Mon Jan 11 02:53:49 PST 2010
Add parsing support for server supported AT commands. Also add
APIs to add/remove notification functions.
---
gatchat/gatserver.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++-
gatchat/gatserver.h | 23 +++++
2 files changed, 247 insertions(+), 1 deletions(-)
diff --git a/gatchat/gatserver.c b/gatchat/gatserver.c
index e861e25..602e509 100644
--- a/gatchat/gatserver.c
+++ b/gatchat/gatserver.c
@@ -67,12 +67,24 @@ struct v250_settings {
unsigned int c108; /* set by &D<val> */
};
+/* AT command that server supported */
+struct at_command {
+ int id;
+ char *prefix;
+ gboolean expect_pdu;
+ GAtServerNotifyFunc notify;
+ gpointer user_data;
+ GDestroyNotify destroy_notify;
+};
+
struct _GAtServer {
gint ref_count; /* Ref count */
struct v250_settings *v250; /* V.250 command setting */
GIOChannel *server_io; /* Server IO */
int server_watch; /* Watch for server IO */
char *modem_path; /* Emulated modem path */
+ GHashTable *command_list; /* Server supported commands */
+ gint next_command_id; /* Id for next command */
GAtDisconnectFunc user_disconnect; /* User disconnect func */
gpointer user_disconnect_data; /* User disconnect data */
GAtDebugFunc debugf; /* Debugging output function */
@@ -128,6 +140,112 @@ static gsize skip_space(const char *buf, gsize pos)
return i;
}
+static inline gboolean is_at_command_prefix(const char c)
+{
+ if (c == '&')
+ return FALSE;
+
+ return g_ascii_ispunct(c);
+}
+
+static gchar *get_at_command_prefix(gchar *command, GAtServerRequestType *type)
+{
+ char c = g_ascii_toupper(*command);
+ gchar *prefix;
+ gchar *equal;
+ gchar *question;
+
+ /* Try to match command in ATA, ATD or ATH */
+ if (c == 'A' || c == 'D' || c == 'H') {
+ *type = G_AT_SERVER_REQUEST_TYPE_ACTION;
+
+ prefix = g_strdup(command);
+ } else if (is_at_command_prefix(c)) {
+ command++;
+
+ equal = strchr(command, '=');
+ question = strchr(command, '?');
+
+ /* Try to match AT+XXX=?, AT+XXX=, AT+XXX? or AT+XXX */
+ if (equal) {
+ if (question)
+ *type = G_AT_SERVER_REQUEST_TYPE_SUPPORT;
+ else
+ *type = G_AT_SERVER_REQUEST_TYPE_SET;
+
+ /* It shouldn't happen but we still check it */
+ if (question != equal + 1)
+ return G_AT_SERVER_REQUEST_TYPE_NONE;
+
+ prefix = g_strndup(command, equal - command);
+ } else if (question) {
+ *type = G_AT_SERVER_REQUEST_TYPE_QUERY;
+
+ prefix = g_strndup(command, question - command);
+ } else {
+ *type = G_AT_SERVER_REQUEST_TYPE_ACTION;
+
+ prefix = g_strdup(command);
+ }
+ }
+
+ return prefix;
+}
+
+static int parse_at_command(GAtServer *server, char *buf)
+{
+ int res = G_AT_SERVER_RESULT_ERROR;
+ GAtServerRequestType type = G_AT_SERVER_REQUEST_TYPE_NONE;
+ gchar t = server->v250->s3;
+ gsize i = 0;
+ gchar *command = NULL;
+ gchar *prefix = NULL;
+ struct at_command *node;
+
+ while (buf[i] != t && buf[i] != ';')
+ i++;
+
+ command = g_strndup(buf, i);
+
+ prefix = get_at_command_prefix(command, &type);
+
+ if (G_AT_SERVER_REQUEST_TYPE_NONE == type)
+ goto out;
+
+ node = g_hash_table_lookup(server->command_list, prefix);
+
+ if (node && node->notify) {
+ if (type == G_AT_SERVER_REQUEST_TYPE_SUPPORT)
+ res = G_AT_SERVER_RESULT_OK;
+ else {
+ GAtResult *result = g_try_new0(GAtResult, 1);
+
+ if (!result)
+ goto out;
+
+ result->lines = g_slist_prepend(NULL, command);
+ result->final_or_pdu = NULL;
+
+ res = node->notify(type, result, node->user_data);
+
+ g_free(result);
+ }
+ }
+
+ /* Compound command */
+ if ((res == G_AT_SERVER_RESULT_OK) && (buf[i] == ';'))
+ res = at_server_parse(server, buf + i);
+
+out:
+ if (prefix)
+ g_free(prefix);
+
+ if (command)
+ g_free(command);
+
+ return res;
+}
+
static int parse_v250_settings(GAtServer *server, char *buf)
{
int res = G_AT_SERVER_RESULT_ERROR;
@@ -150,7 +268,9 @@ static int at_server_parse(GAtServer *server, char *buf)
if (c == ';')
c = buf[++i];
- if (g_ascii_isalpha(c) || c == '&')
+ if (is_at_command_prefix(c) || c == 'A' || c == 'D' || c == 'H')
+ res = parse_at_command(server, buf + i);
+ else if (g_ascii_isalpha(c) || c == '&')
res = parse_v250_settings(server, buf + i);
else if (c == v250->s3)
res = G_AT_SERVER_RESULT_OK;
@@ -305,6 +425,8 @@ GAtServer *g_at_server_new(GIOChannel *io, const char *modem_path)
server->v250 = v250_settings_create();
server->server_io = io;
server->modem_path = g_strdup(modem_path);
+ server->command_list = g_hash_table_new(g_str_hash, g_str_equal);
+ server->next_command_id = 1;
server->user_disconnect = NULL;
server->user_disconnect_data = NULL;
server->debugf = NULL;
@@ -331,6 +453,9 @@ error:
if (server->buf)
ring_buffer_free(server->buf);
+ if (server->command_list)
+ g_hash_table_destroy(server->command_list);
+
if (server->modem_path)
g_free(server->modem_path);
@@ -379,6 +504,10 @@ gboolean g_at_server_shutdown(GAtServer *server)
if (server->v250)
g_free(server->v250);
+ g_at_server_remove_all(server);
+
+ g_hash_table_destroy(server->command_list);
+
ring_buffer_free(server->buf);
server->buf = NULL;
@@ -426,3 +555,97 @@ gboolean g_at_server_set_debug(GAtServer *server, GAtDebugFunc func,
return TRUE;
}
+
+guint g_at_server_add_command(GAtServer *server, const char *prefix,
+ gboolean expect_pdu,
+ GAtServerNotifyFunc notify,
+ gpointer user_data,
+ GDestroyNotify destroy_notify)
+{
+ struct at_command *node;
+
+ if (server == NULL || server->command_list == NULL)
+ return 0;
+
+ if (notify == NULL)
+ return 0;
+
+ if (prefix == NULL || strlen(prefix) == 0)
+ return 0;
+
+ node = g_hash_table_lookup(server->command_list, prefix);
+
+ if (node) {
+ g_hash_table_remove(server->command_list, prefix);
+
+ if (node->destroy_notify)
+ node->destroy_notify(node->user_data);
+
+ g_free(node->prefix);
+
+ g_free(node);
+ }
+
+ node = g_try_new0(struct at_command, 1);
+
+ if (!node)
+ return 0;
+
+ node->id = server->next_command_id++;
+ node->prefix = g_strdup(prefix);
+ node->expect_pdu = expect_pdu;
+ node->notify = notify;
+ node->user_data = user_data;
+ node->destroy_notify = destroy_notify;
+
+ g_hash_table_insert(server->command_list, node->prefix, node);
+
+ return node->id;
+}
+
+static gboolean at_server_remove_command(gpointer key, gpointer value,
+ gpointer user)
+{
+ struct at_command *node = value;
+
+ if (node->destroy_notify)
+ node->destroy_notify(node->user_data);
+
+ g_free(node->prefix);
+
+ g_free(node);
+
+ return TRUE;
+}
+
+gboolean g_at_server_remove_command(GAtServer *server, char *prefix)
+{
+ struct at_command *node;
+ gpointer key = prefix;
+
+ if (!server || !server->command_list)
+ return FALSE;
+
+ node = g_hash_table_lookup(server->command_list, key);
+
+ if (!node)
+ return FALSE;
+
+ g_hash_table_remove(server->command_list, key);
+
+ at_server_remove_command(prefix, node, node);
+
+ return TRUE;
+}
+
+gboolean g_at_server_remove_all(GAtServer *server)
+{
+ if (!server || !server->command_list)
+ return FALSE;
+
+ g_hash_table_foreach_remove(server->command_list,
+ at_server_remove_command, server);
+
+ return TRUE;
+}
+
diff --git a/gatchat/gatserver.h b/gatchat/gatserver.h
index 39916ec..7b68d8a 100644
--- a/gatchat/gatserver.h
+++ b/gatchat/gatserver.h
@@ -45,6 +45,21 @@ enum _GAtServerResultCodes {
G_AT_SERVER_RESULT_CONNECT_EXT,
};
+enum _GAtServerRequestType {
+ G_AT_SERVER_REQUEST_TYPE_NONE = 0,
+ G_AT_SERVER_REQUEST_TYPE_SET,
+ G_AT_SERVER_REQUEST_TYPE_SUPPORT,
+ G_AT_SERVER_REQUEST_TYPE_QUERY,
+ G_AT_SERVER_REQUEST_TYPE_ACTION,
+};
+
+typedef enum _GAtServerRequestType GAtServerRequestType;
+
+typedef int (*GAtServerNotifyFunc)(GAtServerRequestType type,
+ GAtResult *result,
+ gpointer user_data);
+
+
GAtServer *g_at_server_new(GIOChannel *io, const char *modem_path);
GAtServer *g_at_server_ref(GAtServer *server);
@@ -58,6 +73,14 @@ gboolean g_at_server_set_debug(GAtServer *server,
GAtDebugFunc func,
gpointer user);
+guint g_at_server_add_command(GAtServer *server, const char *prefix,
+ gboolean expect_pdu,
+ GAtServerNotifyFunc notify,
+ gpointer user_data,
+ GDestroyNotify destroy);
+gboolean g_at_server_remove_command(GAtServer *server, char *prefix);
+gboolean g_at_server_remove_all(GAtServer *server);
+
#ifdef __cplusplus
}
#endif
--
1.6.3.3
More information about the ofono
mailing list