aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/ublox/mm-plugin-ublox.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/ublox/mm-plugin-ublox.c')
-rw-r--r--src/plugins/ublox/mm-plugin-ublox.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/plugins/ublox/mm-plugin-ublox.c b/src/plugins/ublox/mm-plugin-ublox.c
new file mode 100644
index 00000000..db6d94d3
--- /dev/null
+++ b/src/plugins/ublox/mm-plugin-ublox.c
@@ -0,0 +1,265 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-serial-parsers.h"
+#include "mm-broadband-modem-ublox.h"
+#include "mm-plugin-ublox.h"
+
+G_DEFINE_TYPE (MMPluginUblox, mm_plugin_ublox, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *sysfs_path,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ guint16 subsystem_vendor,
+ GList *probes,
+ GError **error)
+{
+ return MM_BASE_MODEM (mm_broadband_modem_ublox_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+/* Custom init context */
+
+typedef struct {
+ MMPortSerialAt *port;
+ GRegex *ready_regex;
+ guint timeout_id;
+ gint wait_timeout_secs;
+} CustomInitContext;
+
+static void
+custom_init_context_free (CustomInitContext *ctx)
+{
+ g_assert (!ctx->timeout_id);
+ g_regex_unref (ctx->ready_regex);
+ g_object_unref (ctx->port);
+ g_slice_free (CustomInitContext, ctx);
+}
+
+static gboolean
+ublox_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ready_timeout (GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ ctx->timeout_id = 0;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->ready_regex,
+ NULL, NULL, NULL);
+
+ mm_obj_dbg (probe, "timed out waiting for READY unsolicited message");
+
+ /* not an error really, we didn't probe anything yet, that's all */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ready_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ mm_obj_dbg (probe, "received READY: port is AT");
+
+ /* Flag as an AT port right away */
+ mm_port_probe_set_result_at (probe, TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+wait_for_ready (GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ mm_obj_dbg (probe, "waiting for READY unsolicited message...");
+
+ /* Configure a regex on the TTY, so that we stop the custom init
+ * as soon as +READY URC is received */
+ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port,
+ ctx->ready_regex,
+ (MMPortSerialAtUnsolicitedMsgFn) ready_received,
+ task,
+ NULL);
+
+ mm_obj_dbg (probe, "waiting %d seconds for init timeout", ctx->wait_timeout_secs);
+
+ /* Otherwise, let the custom init timeout in some seconds. */
+ ctx->timeout_id = g_timeout_add_seconds (ctx->wait_timeout_secs, (GSourceFunc) ready_timeout, task);
+}
+
+static void
+quick_at_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortProbe *probe;
+ g_autoptr(GError) error = NULL;
+
+ probe = g_task_get_source_object (task);
+
+ mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* On a timeout error, wait for READY URC */
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ wait_for_ready (task);
+ return;
+ }
+ /* On an unknown error, make it fatal */
+ if (!mm_serial_parser_v1_is_known_error (error)) {
+ mm_obj_warn (probe, "custom port initialization logic failed: %s", error->message);
+ goto out;
+ }
+ }
+
+ mm_obj_dbg (probe, "port is AT");
+ mm_port_probe_set_result_at (probe, TRUE);
+
+out:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+ublox_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CustomInitContext *ctx;
+ gint wait_timeout_secs;
+
+ task = g_task_new (probe, cancellable, callback, user_data);
+
+ /* If no explicit READY_DELAY configured, we don't need a custom init procedure */
+ wait_timeout_secs = mm_kernel_device_get_property_as_int (mm_port_probe_peek_port (probe), "ID_MM_UBLOX_PORT_READY_DELAY");
+ if (wait_timeout_secs <= 0) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (CustomInitContext);
+ ctx->wait_timeout_secs = wait_timeout_secs;
+ ctx->port = g_object_ref (port);
+ ctx->ready_regex = g_regex_new ("\\r\\n\\+AT:\\s*READY\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) custom_init_context_free);
+
+ /* If the device hasn't been plugged in right away, we assume it was already
+ * running for some time. We validate the assumption with a quick AT probe,
+ * and if it times out, we run the explicit READY wait from scratch (e.g.
+ * to cope with the case where MM starts after the TTY has been exposed but
+ * where the device was also just reseted) */
+ if (!mm_device_get_hotplugged (mm_port_probe_peek_device (probe))) {
+ mm_port_serial_at_command (ctx->port,
+ "AT",
+ 1,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)quick_at_ready,
+ task);
+ return;
+ }
+
+ /* Device hotplugged and has a defined ready delay, wait for READY URC */
+ wait_for_ready (task);
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", NULL };
+ static const guint16 vendor_ids[] = { 0x1546, 0 };
+ static const gchar *vendor_strings[] = { "u-blox", NULL };
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (ublox_custom_init),
+ .finish = G_CALLBACK (ublox_custom_init_finish),
+ };
+
+ return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_UBLOX,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_SEND_DELAY, (guint64) 0,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ NULL));
+}
+
+static void
+mm_plugin_ublox_init (MMPluginUblox *self)
+{
+}
+
+static void
+mm_plugin_ublox_class_init (MMPluginUbloxClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}