diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/mm-at.c | 258 | ||||
-rw-r--r-- | src/mm-at.h | 90 |
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 */ + |