Files
ModemManager/src/plugins/intel/mm-bearer-xmm7360.c
T
2025-05-09 07:31:31 +02:00

767 lines
26 KiB
C

/* -*- 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;
}