aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2011-11-22 16:52:19 +0100
committerAleksander Morgado <aleksander@lanedo.com>2012-03-15 14:14:21 +0100
commitf15daaf587ae296c6bec6f79a24cecb1860508bb (patch)
tree3a2b0828970280b21a08b145ea3274727613b231
parent07640b0c741292bba5138110caeb8961a2e82896 (diff)
core: new AT command and sequence processors
This setup, allows: - Running a single command and processing its result. - Running a set of N commands, providing a global result after all have been executed. - Running a set of N commands out of M (N<M), where the global result is obtained without having executed all configured commands. This is useful when probing, for example.
-rw-r--r--src/Makefile.am2
-rw-r--r--src/mm-at.c258
-rw-r--r--src/mm-at.h90
3 files changed, 350 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 4c08b736..562e3f56 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -101,6 +101,8 @@ modem_manager_SOURCES = \
main.c \
mm-log.c \
mm-log.h \
+ mm-at.h \
+ mm-at.c \
mm-callback-info.c \
mm-callback-info.h \
$(auth_sources) \
diff --git a/src/mm-at.c b/src/mm-at.c
new file mode 100644
index 00000000..81235377
--- /dev/null
+++ b/src/mm-at.c
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <ModemManager.h>
+
+#include "mm-at.h"
+#include "mm-errors-types.h"
+
+typedef struct {
+ GObject *owner;
+ MMAtSerialPort *port;
+ GCancellable *cancellable;
+ MMAtCommand *current;
+ MMAtCommand *sequence;
+ gboolean sequence_free;
+ GSimpleAsyncResult *result;
+ gchar *result_signature;
+ gpointer response_processor_context;
+} AtSequenceContext;
+
+static void
+at_sequence_free (MMAtCommand *sequence)
+{
+ MMAtCommand *it;
+
+ for (it = sequence; it->command; it++)
+ g_free (it->command);
+
+ g_free (sequence);
+}
+
+static void
+at_sequence_context_free (AtSequenceContext *ctx)
+{
+ if (ctx->sequence_free)
+ at_sequence_free (ctx->sequence);
+ if (ctx->owner)
+ g_object_unref (ctx->owner);
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ g_free (ctx->result_signature);
+ g_object_unref (ctx->port);
+ g_object_unref (ctx->result);
+ g_free (ctx);
+}
+
+static void
+at_sequence_parse_response (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ AtSequenceContext *ctx)
+{
+ GVariant *result = NULL;
+ GError *result_error = NULL;
+ gboolean continue_sequence;
+
+ /* Cancelled? */
+ if (g_cancellable_is_cancelled (ctx->cancellable)) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "AT sequence was cancelled");
+ g_simple_async_result_complete (ctx->result);
+ at_sequence_context_free (ctx);
+ return;
+ }
+
+ /* Translate the AT response into a GVariant with the expected signature */
+ if (!ctx->current->response_processor)
+ /* No need to process response, go on to next command */
+ continue_sequence = TRUE;
+ else {
+ /* Response processor will tell us if we need to keep on the sequence */
+ continue_sequence = !ctx->current->response_processor (
+ ctx->owner,
+ ctx->response_processor_context,
+ ctx->current->command,
+ response->str,
+ error,
+ &result,
+ &result_error);
+ /* Were we told to abort the sequence? */
+ if (result_error) {
+ g_assert (result == NULL);
+ g_simple_async_result_take_error (ctx->result, result_error);
+ g_simple_async_result_complete (ctx->result);
+ at_sequence_context_free (ctx);
+ return;
+ }
+ }
+
+ if (continue_sequence) {
+ g_assert (result == NULL);
+ ctx->current++;
+ if (ctx->current->command) {
+ /* Schedule the next command in the probing group */
+ mm_at_serial_port_queue_command (
+ ctx->port,
+ ctx->current->command,
+ ctx->current->timeout,
+ (MMAtSerialResponseFn)at_sequence_parse_response,
+ ctx);
+ return;
+ }
+
+ /* On last command, end. */
+ }
+
+ /* Maybe we do not expect/need any result */
+ if (!ctx->result_signature) {
+ g_assert (result == NULL);
+ g_simple_async_result_set_op_res_gpointer (ctx->result, NULL, NULL);
+ }
+ /* If we do expect one but got none, set error */
+ else if (!result) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Expecting result type '%s', got none",
+ ctx->result_signature);
+ }
+ /* Check if the result we got is of the type we expected */
+ else if (!g_str_equal (g_variant_get_type_string (result),
+ ctx->result_signature)) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Expecting result type '%s', got '%s'",
+ ctx->result_signature,
+ g_variant_get_type_string (result));
+ }
+ /* We got a valid expected reply */
+ else {
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ g_variant_ref (result),
+ (GDestroyNotify)g_variant_unref);
+ }
+
+ if (result)
+ g_variant_unref (result);
+ g_simple_async_result_complete (ctx->result);
+ at_sequence_context_free (ctx);
+}
+
+GVariant *
+mm_at_sequence_finish (GObject *owner,
+ GAsyncResult *res,
+ GError **error)
+{
+ GVariant *result;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
+ error))
+ return NULL;
+
+ result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+
+ return (result ? g_variant_ref (result) : NULL);
+}
+
+void
+mm_at_sequence (GObject *owner,
+ MMAtSerialPort *port,
+ MMAtCommand *sequence,
+ gpointer response_processor_context,
+ gboolean sequence_free,
+ const gchar *result_signature,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ AtSequenceContext *ctx;
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (port));
+ g_return_if_fail (sequence != NULL);
+ g_return_if_fail (callback != NULL);
+
+ ctx = g_new (AtSequenceContext, 1);
+ ctx->owner = owner ? g_object_ref (owner) : NULL;
+ ctx->port = g_object_ref (port);
+ ctx->result_signature = g_strdup (result_signature);
+ ctx->cancellable = (cancellable ?
+ g_object_ref (cancellable) :
+ NULL);
+ ctx->result = g_simple_async_result_new (owner,
+ callback,
+ user_data,
+ mm_at_sequence);
+ ctx->current = ctx->sequence = sequence;
+ ctx->sequence_free = sequence_free;
+ ctx->response_processor_context = response_processor_context;
+
+ mm_at_serial_port_queue_command (
+ ctx->port,
+ ctx->current->command,
+ ctx->current->timeout,
+ (MMAtSerialResponseFn)at_sequence_parse_response,
+ ctx);
+}
+
+GVariant *
+mm_at_command_finish (GObject *owner,
+ GAsyncResult *res,
+ GError **error)
+{
+ return mm_at_sequence_finish (owner, res, error);
+}
+
+void
+mm_at_command (GObject *owner,
+ MMAtSerialPort *port,
+ const gchar *command,
+ guint timeout,
+ const MMAtResponseProcessor response_processor,
+ gpointer response_processor_context,
+ const gchar *result_signature,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMAtCommand *sequence;
+
+ g_return_if_fail (MM_IS_AT_SERIAL_PORT (port));
+ g_return_if_fail (command != NULL);
+ g_return_if_fail (response_processor != NULL ||
+ result_signature == NULL);
+ g_return_if_fail (callback != NULL);
+
+ sequence = g_new0 (MMAtCommand, 2);
+ sequence->command = g_strdup (command);
+ sequence->timeout = timeout;
+ sequence->response_processor = response_processor;
+
+ mm_at_sequence (owner,
+ port,
+ sequence,
+ response_processor_context,
+ TRUE,
+ result_signature,
+ cancellable,
+ callback,
+ user_data);
+}
diff --git a/src/mm-at.h b/src/mm-at.h
new file mode 100644
index 00000000..c0f24dd5
--- /dev/null
+++ b/src/mm-at.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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:
+ *
+ * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ */
+
+#ifndef MM_AT_H
+#define MM_AT_H
+
+#include <gio/gio.h>
+
+#include "mm-at-serial-port.h"
+
+/*
+ * The expected result depends on the specific operation, so the GVariant
+ * created by the response processor needs to match the signature given
+ * in this setup.
+ *
+ * TRUE must be returned when the operation is to be considered successful,
+ * and a result (NULL allowed, if no signature specified) is given.
+ *
+ * FALSE must be returned when:
+ * - A GError is propagated into result_error, which will be treated as a
+ * critical error and therefore the operation will be aborted.
+ * - When no result_error is given, to instruct the operation to go on with
+ * the next scheduled command.
+ *
+ * This setup, therefore allows:
+ * - Running a single command and processing its result.
+ * - Running a set of N commands, providing a global result after all have
+ * been executed.
+ * - Running a set of N commands out of M (N<M), where the global result is
+ * obtained without having executed all configured commands.
+ */
+typedef gboolean (* MMAtResponseProcessor) (GObject *owner,
+ gpointer response_processor_context,
+ const gchar *command,
+ const gchar *response,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
+
+/* Struct to configure AT command operations */
+typedef struct {
+ /* The AT command */
+ gchar *command;
+ /* Timeout of the command, in seconds */
+ guint timeout;
+ /* The response processor */
+ MMAtResponseProcessor response_processor;
+} MMAtCommand;
+
+void mm_at_sequence (GObject *owner,
+ MMAtSerialPort *port,
+ MMAtCommand *sequence,
+ gpointer response_processor_context,
+ gboolean free_sequence,
+ const gchar *result_signature,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GVariant *mm_at_sequence_finish (GObject *owner,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_at_command (GObject *owner,
+ MMAtSerialPort *port,
+ const gchar *command,
+ guint timeout,
+ const MMAtResponseProcessor response_processor,
+ gpointer response_processor_context,
+ const gchar *result_signature,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GVariant *mm_at_command_finish (GObject *owner,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_AT_H */
+