[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