aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/intel/mm-bearer-xmm7360.c
diff options
context:
space:
mode:
authorThomas Vogt <acc-github@tovotu.de>2024-06-28 15:07:51 +0200
committerThomas Vogt <acc-github@tovotu.de>2025-05-09 07:31:31 +0200
commit4cae0406452ef4621aa7cc73f9d7a54db2ca0fb1 (patch)
treee7331cd0736ab0d005717e76bfb0f3351e6f9b74 /src/plugins/intel/mm-bearer-xmm7360.c
parentd165d61a9515061837ac12054d15dbeaeb134adf (diff)
intel: implement support for RPC-powered xmm7360
Signed-off-by: Thomas Vogt <acc-github@tovotu.de>
Diffstat (limited to 'src/plugins/intel/mm-bearer-xmm7360.c')
-rw-r--r--src/plugins/intel/mm-bearer-xmm7360.c766
1 files changed, 766 insertions, 0 deletions
diff --git a/src/plugins/intel/mm-bearer-xmm7360.c b/src/plugins/intel/mm-bearer-xmm7360.c
new file mode 100644
index 00000000..10dd2c26
--- /dev/null
+++ b/src/plugins/intel/mm-bearer-xmm7360.c
@@ -0,0 +1,766 @@
+/* -*- 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) 2019 James Wah
+ * Copyright (C) 2020 Marinus Enzinger <marinus@enzingerm.de>
+ * Copyright (C) 2023 Shane Parslow
+ * Copyright (C) 2024 Thomas Vogt
+ */
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+
+#include "mm-bearer-xmm7360.h"
+#include "mm-broadband-modem-xmm7360.h"
+#include "mm-broadband-modem-xmm7360-rpc.h"
+#include "mm-port-serial-xmmrpc-xmm7360.h"
+
+G_DEFINE_TYPE (MMBearerXmm7360, mm_bearer_xmm7360, MM_TYPE_BASE_BEARER)
+
+struct _MMBearerXmm7360Private {
+ gboolean is_connected;
+};
+
+/*****************************************************************************/
+/* Connect */
+
+typedef struct {
+ MMPort *data;
+ MMPortSerialXmmrpcXmm7360 *port;
+ guint unsol_handler_id;
+ gboolean is_attach_allowed;
+ gboolean is_attached;
+ guint attach_attempts;
+ guint attach_allowed_timeout_id;
+ Xmm7360RpcResponse *ps_connect_response;
+ GInetAddress *ip;
+ GPtrArray *dns;
+} ConnectContext;
+
+static void
+connect_context_free (ConnectContext *ctx)
+{
+ if (ctx->ps_connect_response)
+ xmm7360_rpc_response_free (ctx->ps_connect_response);
+ if (ctx->attach_allowed_timeout_id)
+ g_source_remove (ctx->attach_allowed_timeout_id);
+ if (ctx->unsol_handler_id) {
+ mm_port_serial_xmmrpc_xmm7360_enable_unsolicited_msg_handler (
+ ctx->port,
+ ctx->unsol_handler_id,
+ FALSE);
+ }
+ g_clear_object (&ctx->ip);
+ if (ctx->dns)
+ g_ptr_array_free (ctx->dns, TRUE);
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
+ g_clear_object (&ctx->port);
+ g_clear_object (&ctx->data);
+ g_slice_free (ConnectContext, ctx);
+}
+
+static MMBearerConnectResult *
+connect_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+xmm7360_connect_apn_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMBearerXmm7360 *self;
+ MMBearerConnectResult *connect_result;
+
+ self = g_task_get_source_object (task);
+
+ connect_result = g_task_propagate_pointer (G_TASK (res), &error);
+ if (error) {
+ g_task_return_error (task, error);
+ } else {
+ self->priv->is_connected = TRUE;
+ g_task_return_pointer (task,
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+ }
+ g_object_unref (task);
+}
+
+static void
+ps_connect_setup_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ ConnectContext *ctx;
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+ g_autoptr(MMBearerIpConfig) ip_config = NULL;
+ g_autofree gchar *ipaddr = NULL;
+ g_auto(GStrv) dnsaddrs = NULL;
+ GInetAddress *dns;
+ guint i;
+ guint n;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* we get static IPs to set */
+ ip_config = mm_bearer_ip_config_new ();
+ ipaddr = g_inet_address_to_string (ctx->ip);
+ dnsaddrs = g_new0 (gchar *, ctx->dns->len + 1);
+ for (i = 0, n = 0; i < ctx->dns->len; i++) {
+ dns = g_ptr_array_index (ctx->dns, i);
+ if (g_inet_address_get_family (dns) == G_SOCKET_FAMILY_IPV4
+ && !g_inet_address_get_is_any (dns)) {
+ dnsaddrs[n++] = g_inet_address_to_string (dns);
+ }
+ }
+ mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC);
+ mm_bearer_ip_config_set_address (ip_config, ipaddr);
+ mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dnsaddrs);
+
+ g_task_return_pointer (task,
+ mm_bearer_connect_result_new (ctx->data, ip_config, NULL),
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
+}
+
+static void
+ps_connect_to_datachannel_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ ConnectContext *ctx;
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+ g_autoptr(GByteArray) connect_setup_body = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ connect_setup_body = g_byte_array_new ();
+ g_byte_array_append (connect_setup_body,
+ ctx->ps_connect_response->body->data,
+ ctx->ps_connect_response->body->len - 6);
+ g_byte_array_append (connect_setup_body,
+ response->body->data,
+ response->body->len);
+ xmm7360_byte_array_append_asn_int4 (connect_setup_body, 0);
+
+ mm_broadband_modem_xmm7360_rpc_command_full (modem,
+ ctx->port,
+ XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_SETUP_REQ,
+ FALSE,
+ connect_setup_body,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)ps_connect_setup_ready,
+ task);
+}
+
+static GByteArray *
+pack_uta_rpc_ps_connect_to_datachannel_req (void)
+{
+ static const Xmm7360RpcMsgArg args[] = {
+ /* size is length of string + null byte */
+ { XMM7360_RPC_MSG_ARG_TYPE_STRING, { .string = "/sioscc/PCIE/IOSM/IPS/0" }, 24 },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ };
+ return xmm7360_rpc_args_to_byte_array (args);
+}
+
+static void
+ps_connect_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ ConnectContext *ctx;
+ Xmm7360RpcResponse *response = NULL;
+ g_autoptr(GByteArray) body = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ xmm7360_rpc_response_free (response);
+ return;
+ }
+
+ ctx->ps_connect_response = response;
+
+ body = pack_uta_rpc_ps_connect_to_datachannel_req ();
+
+ mm_broadband_modem_xmm7360_rpc_command_full (modem,
+ ctx->port,
+ XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_TO_DATACHANNEL_REQ,
+ FALSE,
+ body,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)ps_connect_to_datachannel_ready,
+ task);
+}
+
+static GByteArray *
+pack_uta_ms_call_ps_connect_req (void)
+{
+ static const Xmm7360RpcMsgArg args[] = {
+ { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 6 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ };
+ return xmm7360_rpc_args_to_byte_array (args);
+}
+
+static void
+get_dns_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ ConnectContext *ctx;
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+ Xmm7360RpcMsgArg *arg;
+ guint i;
+ g_autoptr(GByteArray) body = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (i = 1; i < 17; i += 2) {
+ /* iterate over pairs of args: IP [i] and IP format (v4(1) or v6(2) or MISSING(0)) [i+1] */
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, i + 1);
+ if (XMM7360_RPC_MSG_ARG_GET_INT (arg) == 1) {
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, i);
+ g_assert (arg->size >= 4);
+ g_ptr_array_add (ctx->dns, g_inet_address_new_from_bytes ((const guint8 *)arg->value.string,
+ G_SOCKET_FAMILY_IPV4));
+ } else if (XMM7360_RPC_MSG_ARG_GET_INT (arg) == 2) {
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, i);
+ g_assert (arg->size >= 16);
+ g_ptr_array_add (ctx->dns, g_inet_address_new_from_bytes ((const guint8 *)arg->value.string,
+ G_SOCKET_FAMILY_IPV6));
+ }
+ }
+
+ body = pack_uta_ms_call_ps_connect_req ();
+
+ mm_broadband_modem_xmm7360_rpc_command_full (modem,
+ ctx->port,
+ XMM7360_RPC_CALL_UTA_MS_CALL_PS_CONNECT_REQ,
+ TRUE,
+ body,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)ps_connect_ready,
+ task);
+}
+
+static GByteArray *
+pack_uta_ms_call_ps_get_negotiated_dns_req (void)
+{
+ static const Xmm7360RpcMsgArg args[] = {
+ { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ };
+ return xmm7360_rpc_args_to_byte_array (args);
+}
+
+static void
+get_ip_addr_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ ConnectContext *ctx;
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+ Xmm7360RpcMsgArg *arg;
+ gint i;
+ guint32 *ip_bytes;
+ g_autoptr(GByteArray) body = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 1);
+ if (arg->size < 12) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "The IP address field is invalid (too short)");
+ g_object_unref (task);
+ return;
+ }
+
+ /* the STRING arg contains three IP addresses, we only use the last non-zero address */
+ ip_bytes = (guint32 *)arg->value.string;
+ for (i = 2; i >= 0; i--) {
+ if (ip_bytes[i] != 0) {
+ ctx->ip = g_inet_address_new_from_bytes ((const guint8 *)&ip_bytes[i],
+ G_SOCKET_FAMILY_IPV4);
+ break;
+ }
+ }
+
+ if (ctx->ip == NULL) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "The IP address field is invalid (all zeros)");
+ g_object_unref (task);
+ return;
+ }
+
+ body = pack_uta_ms_call_ps_get_negotiated_dns_req ();
+
+ mm_broadband_modem_xmm7360_rpc_command_full (modem,
+ ctx->port,
+ XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEGOTIATED_DNS_REQ,
+ TRUE,
+ body,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_dns_ready,
+ task);
+}
+
+static GByteArray *
+pack_uta_ms_call_ps_get_neg_ip_addr_req (void)
+{
+ static const Xmm7360RpcMsgArg args[] = {
+ { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ };
+ return xmm7360_rpc_args_to_byte_array (args);
+}
+
+static gboolean
+get_ip_config (GTask *task)
+{
+ ConnectContext *ctx;
+ MMBroadbandModemXmm7360 *modem;
+ g_autoptr(GByteArray) body = NULL;
+
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "operation has been cancelled");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ modem = g_task_get_source_object (task);
+
+ body = pack_uta_ms_call_ps_get_neg_ip_addr_req ();
+
+ mm_broadband_modem_xmm7360_rpc_command_full (modem,
+ ctx->port,
+ XMM7360_RPC_CALL_UTA_MS_CALL_PS_GET_NEG_IP_ADDR_REQ,
+ TRUE,
+ body,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_ip_addr_ready,
+ task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+attach_allowed_timeout_cb (GTask *task)
+{
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Connecting timed out (waiting for attach-allowed)");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void net_attach_command (GTask *task);
+
+static void
+net_attach_command_ready (MMBroadbandModemXmm7360 *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ ConnectContext *ctx;
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+ Xmm7360RpcMsgArg *arg;
+ gint status;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_broadband_modem_xmm7360_rpc_command_finish (modem, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (response->content->len < 2) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "The response for net-attach is invalid (too short)");
+ g_object_unref (task);
+ return;
+ }
+
+ /* status code */
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 1);
+ g_assert (arg->type == XMM7360_RPC_MSG_ARG_TYPE_LONG);
+ status = XMM7360_RPC_MSG_ARG_GET_INT (arg);
+
+ if (status != (gint32)0xffffffff) {
+ ctx->is_attached = TRUE;
+ /* give the modem a second before requesting the IP */
+ g_timeout_add_seconds (1, (GSourceFunc)get_ip_config, task);
+ } else if (ctx->is_attach_allowed && ctx->attach_attempts < 2) {
+ /* immediately try a second time if it should have been allowed */
+ ctx->is_attach_allowed = FALSE;
+ net_attach_command (task);
+ } else if (ctx->attach_attempts < 3) {
+ /* give up if we do not receive an attach-allowed unsolicited message within 5 seconds */
+ ctx->attach_allowed_timeout_id = g_timeout_add_seconds (5, (GSourceFunc)attach_allowed_timeout_cb, task);
+ } else {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Giving up on attach-net operation after three failed attempts");
+ g_object_unref (task);
+ }
+}
+
+static GByteArray *
+pack_uta_ms_net_attach_req (void)
+{
+ static const Xmm7360RpcMsgArg args[] = {
+ { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0xffff } },
+ { XMM7360_RPC_MSG_ARG_TYPE_SHORT, { .s = 0xffff } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ };
+ return xmm7360_rpc_args_to_byte_array (args);
+}
+
+static void
+net_attach_command (GTask *task)
+{
+ ConnectContext *ctx;
+ MMBroadbandModemXmm7360 *modem;
+ g_autoptr(GByteArray) body = NULL;
+
+ ctx = g_task_get_task_data (task);
+ ctx->attach_attempts++;
+
+ modem = g_task_get_source_object (task);
+
+ body = pack_uta_ms_net_attach_req ();
+
+ mm_broadband_modem_xmm7360_rpc_command_full (modem,
+ ctx->port,
+ XMM7360_RPC_CALL_UTA_MS_NET_ATTACH_REQ,
+ TRUE,
+ body,
+ 3,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)net_attach_command_ready,
+ task);
+}
+
+static gboolean
+connect_unsol_handler (MMPortSerialXmmrpcXmm7360 *port,
+ Xmm7360RpcResponse *response,
+ GTask *task)
+{
+ Xmm7360RpcMsgArg *arg;
+ ConnectContext *ctx;
+
+ if (response->unsol_id != XMM7360_RPC_UNSOL_UTA_MS_NET_IS_ATTACH_ALLOWED_IND_CB) {
+ return FALSE;
+ }
+
+ if (response->content->len <= 2) {
+ mm_obj_dbg (port, "Ignoring invalid is-attach-allowed message (too short)");
+ return TRUE;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->is_attached) {
+ /* already attached, ignore the attach-allowed status message */
+ return TRUE;
+ }
+
+ arg = (Xmm7360RpcMsgArg *) g_ptr_array_index (response->content, 2);
+ if (XMM7360_RPC_MSG_ARG_GET_INT (arg)) {
+ ctx->is_attach_allowed = TRUE;
+ if (ctx->attach_allowed_timeout_id) {
+ g_source_remove (ctx->attach_allowed_timeout_id);
+ ctx->attach_allowed_timeout_id = 0;
+ /* attach-net is allowed now, retry */
+ net_attach_command (task);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+xmm7360_connect (MMBroadbandModemXmm7360 *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GTask *task;
+ ConnectContext *ctx;
+
+ task = g_task_new (modem, cancellable, callback, user_data);
+
+ ctx = g_slice_new0 (ConnectContext);
+ ctx->port = mm_broadband_modem_xmm7360_get_port_xmmrpc (modem);
+ ctx->dns = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
+
+ /* Grab a data port */
+ ctx->data = mm_base_modem_get_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
+ if (!ctx->data) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Open XMMRPC port for initialization */
+ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) {
+ g_prefix_error (&error, "Couldn't open XMMRPC port for connection setup: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->unsol_handler_id = mm_port_serial_xmmrpc_xmm7360_add_unsolicited_msg_handler (
+ ctx->port,
+ (MMPortSerialXmmrpcXmm7360UnsolicitedMsgFn)connect_unsol_handler,
+ task,
+ NULL);
+
+ net_attach_command (task);
+}
+
+static void
+connect (MMBaseBearer *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ g_autoptr(MMBaseModem) modem = NULL;
+
+ /* Get the owner modem object */
+ g_object_get (self,
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+ g_assert (modem != NULL);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ /* the connect routine is independent of the bearer object */
+ xmm7360_connect (MM_BROADBAND_MODEM_XMM7360 (modem),
+ cancellable,
+ (GAsyncReadyCallback)xmm7360_connect_apn_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Disconnect */
+
+static gboolean
+disconnect_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disconnect_sequence_ready (MMBroadbandModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(Xmm7360RpcResponse) response = NULL;
+
+ response = mm_broadband_modem_xmm7360_rpc_sequence_finish (MM_BROADBAND_MODEM_XMM7360 (modem),
+ res,
+ &error);
+
+ if (error) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to complete disconnect sequence: %s", error->message);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static const MMBroadbandModemXmm7360RpcCommand disconnect_sequence[] = {
+ {
+ XMM7360_RPC_CALL_UTA_MS_CALL_PS_DEACTIVATE_REQ,
+ TRUE,
+ (Xmm7360RpcMsgArg[]) {
+ { XMM7360_RPC_MSG_ARG_TYPE_BYTE, { .b = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ },
+ 3,
+ FALSE,
+ mm_broadband_modem_xmm7360_rpc_response_processor_continue_on_success
+ /* response will be: L(0x0) L(0x0) L(0x5dffffff) L(0x0) (meaning unknown) */
+ },
+ {
+ XMM7360_RPC_CALL_UTA_RPC_PS_CONNECT_RELEASE_REQ,
+ FALSE,
+ (Xmm7360RpcMsgArg[]) {
+ /* the meaning of this value is unknown, but it is used by the Windows driver */
+ { XMM7360_RPC_MSG_ARG_TYPE_LONG, { .l = 0x20017 } },
+ { XMM7360_RPC_MSG_ARG_TYPE_UNKNOWN },
+ },
+ 3,
+ FALSE,
+ mm_broadband_modem_xmm7360_rpc_response_processor_final
+ /* response will be: L(0x0) (meaning unknown) */
+ },
+ { 0 }
+};
+
+static void
+disconnect (MMBaseBearer *bearer,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ g_autoptr(MMBaseModem) modem = NULL;
+ g_autoptr(MMPortSerialXmmrpcXmm7360) port = NULL;
+
+ task = g_task_new (bearer, NULL, callback, user_data);
+
+ /* Get the owner modem object */
+ g_object_get (bearer, MM_BASE_BEARER_MODEM, &modem, NULL);
+ g_assert (modem != NULL);
+
+ port = mm_broadband_modem_xmm7360_get_port_xmmrpc (MM_BROADBAND_MODEM_XMM7360 (modem));
+
+ mm_broadband_modem_xmm7360_rpc_sequence_full (MM_BROADBAND_MODEM_XMM7360 (modem),
+ port,
+ disconnect_sequence,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)disconnect_sequence_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMBaseBearer *
+mm_bearer_xmm7360_new (MMBroadbandModemXmm7360 *modem,
+ MMBearerProperties *config)
+{
+ MMBaseBearer *base_bearer;
+ MMBearerXmm7360 *bearer;
+
+ /* The Xmm7360 bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer)
+ * and that means that the object is not async-initable, so we just use
+ * g_object_new here */
+ bearer = g_object_new (MM_TYPE_BEARER_XMM7360,
+ MM_BASE_BEARER_MODEM, modem,
+ MM_BASE_BEARER_CONFIG, config,
+ NULL);
+
+ base_bearer = MM_BASE_BEARER (bearer);
+ /* Only export valid bearers */
+ mm_base_bearer_export (base_bearer);
+
+ return base_bearer;
+}
+
+static void
+mm_bearer_xmm7360_init (MMBearerXmm7360 *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BEARER_XMM7360,
+ MMBearerXmm7360Private);
+}
+
+static void
+mm_bearer_xmm7360_class_init (MMBearerXmm7360Class *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBearerXmm7360Private));
+
+ base_bearer_class->connect = connect;
+ base_bearer_class->connect_finish = connect_finish;
+
+ base_bearer_class->disconnect = disconnect;
+ base_bearer_class->disconnect_finish = disconnect_finish;
+}