aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/cinterion/mm-broadband-modem-cinterion.c154
1 files changed, 154 insertions, 0 deletions
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index f86460a5..b5fa5837 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -277,6 +277,158 @@ modem_power_down (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Modem Power Off */
+
+#define MAX_POWER_OFF_WAIT_TIME_SECS 20
+
+typedef struct {
+ MMBroadbandModemCinterion *self;
+ MMPortSerialAt *port;
+ GSimpleAsyncResult *result;
+ GRegex *shutdown_regex;
+ gboolean shutdown_received;
+ gboolean smso_replied;
+ gboolean serial_open;
+ guint timeout_id;
+} PowerOffContext;
+
+static void
+power_off_context_complete_and_free (PowerOffContext *ctx)
+{
+ if (ctx->serial_open)
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
+ if (ctx->timeout_id)
+ g_source_remove (ctx->timeout_id);
+ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL);
+ g_object_unref (ctx->port);
+ g_object_unref (ctx->self);
+ g_regex_unref (ctx->shutdown_regex);
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_slice_free (PowerOffContext, ctx);
+}
+
+static gboolean
+modem_power_off_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void
+complete_power_off (PowerOffContext *ctx)
+{
+ if (!ctx->shutdown_received || !ctx->smso_replied)
+ return;
+
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ power_off_context_complete_and_free (ctx);
+}
+
+static void
+smso_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ PowerOffContext *ctx)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ power_off_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Set as replied */
+ ctx->smso_replied = TRUE;
+ complete_power_off (ctx);
+}
+
+static void
+shutdown_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ PowerOffContext *ctx)
+{
+ /* Cleanup handler */
+ mm_port_serial_at_add_unsolicited_msg_handler (port, ctx->shutdown_regex, NULL, NULL, NULL);
+ /* Set as received */
+ ctx->shutdown_received = TRUE;
+ complete_power_off (ctx);
+}
+
+static gboolean
+power_off_timeout_cb (PowerOffContext *ctx)
+{
+ ctx->timeout_id = 0;
+
+ /* The SMSO reply should have come earlier */
+ g_warn_if_fail (ctx->smso_replied == TRUE);
+
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Power off operation timed out");
+ power_off_context_complete_and_free (ctx);
+
+ return FALSE;
+}
+
+static void
+modem_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ PowerOffContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_slice_new0 (PowerOffContext);
+ ctx->self = g_object_ref (self);
+ ctx->port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_power_off);
+ ctx->shutdown_regex = g_regex_new ("\\r\\n\\^SHUTDOWN\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ ctx->timeout_id = g_timeout_add_seconds (MAX_POWER_OFF_WAIT_TIME_SECS,
+ (GSourceFunc)power_off_timeout_cb,
+ ctx);
+
+ /* We'll need to wait for a ^SHUTDOWN before returning the action, which is
+ * when the modem tells us that it is ready to be shutdown */
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ctx->port,
+ ctx->shutdown_regex,
+ (MMPortSerialAtUnsolicitedMsgFn)shutdown_received,
+ ctx,
+ NULL);
+
+ /* In order to get the ^SHUTDOWN notification, we must keep the port open
+ * during the wait time */
+ ctx->serial_open = mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error);
+ if (G_UNLIKELY (error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ power_off_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Note: we'll use a timeout < MAX_POWER_OFF_WAIT_TIME_SECS for the AT command,
+ * so we're sure that the AT command reply will always come before the timeout
+ * fires */
+ g_assert (MAX_POWER_OFF_WAIT_TIME_SECS > 5);
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->port,
+ "^SMSO",
+ 5,
+ FALSE, /* allow_cached */
+ FALSE, /* is_raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)smso_ready,
+ ctx);
+}
+
+/*****************************************************************************/
/* ACCESS TECHNOLOGIES */
static gboolean
@@ -1283,6 +1435,8 @@ iface_modem_init (MMIfaceModem *iface)
iface->setup_flow_control_finish = setup_flow_control_finish;
iface->modem_power_down = modem_power_down;
iface->modem_power_down_finish = modem_power_down_finish;
+ iface->modem_power_off = modem_power_off;
+ iface->modem_power_off_finish = modem_power_off_finish;
}
static void