/* -*- 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 */ #include #include #include #include #include #include #include #include #define _LIBMM_INSIDE_MM #include #include "mm-broadband-bearer-ublox.h" #include "mm-base-modem-at.h" #include "mm-log-object.h" #include "mm-ublox-enums-types.h" #include "mm-modem-helpers.h" #include "mm-modem-helpers-ublox.h" G_DEFINE_TYPE (MMBroadbandBearerUblox, mm_broadband_bearer_ublox, MM_TYPE_BROADBAND_BEARER) enum { PROP_0, PROP_USB_PROFILE, PROP_NETWORKING_MODE, PROP_LAST }; static GParamSpec *properties[PROP_LAST]; struct _MMBroadbandBearerUbloxPrivate { MMUbloxUsbProfile profile; MMUbloxNetworkingMode mode; MMUbloxBearerAllowedAuth allowed_auths; FeatureSupport statistics; FeatureSupport cedata; }; /*****************************************************************************/ /* Common connection context and task */ typedef struct { MMBroadbandModem *modem; MMPortSerialAt *primary; MMPort *data; guint cid; gboolean auth_required; MMBearerIpConfig *ip_config; /* For IPv4 settings */ } CommonConnectContext; static void common_connect_context_free (CommonConnectContext *ctx) { if (ctx->ip_config) g_object_unref (ctx->ip_config); if (ctx->data) g_object_unref (ctx->data); g_object_unref (ctx->modem); g_object_unref (ctx->primary); g_slice_free (CommonConnectContext, ctx); } static GTask * common_connect_task_new (MMBroadbandBearerUblox *self, MMBroadbandModem *modem, MMPortSerialAt *primary, guint cid, MMPort *data, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { CommonConnectContext *ctx; GTask *task; ctx = g_slice_new0 (CommonConnectContext); ctx->modem = g_object_ref (modem); ctx->primary = g_object_ref (primary); ctx->cid = cid; task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, ctx, (GDestroyNotify) common_connect_context_free); /* We need a net data port */ if (data) ctx->data = g_object_ref (data); else { 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 NULL; } } return task; } /*****************************************************************************/ /* 3GPP IP config (sub-step of the 3GPP Connection sequence) */ static gboolean get_ip_config_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, MMBearerIpConfig **ipv4_config, MMBearerIpConfig **ipv6_config, GError **error) { MMBearerConnectResult *configs; MMBearerIpConfig *ipv4; configs = g_task_propagate_pointer (G_TASK (res), error); if (!configs) return FALSE; /* Just IPv4 for now */ ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs); g_assert (ipv4); if (ipv4_config) *ipv4_config = g_object_ref (ipv4); if (ipv6_config) *ipv6_config = NULL; mm_bearer_connect_result_unref (configs); return TRUE; } static void complete_get_ip_config_3gpp (GTask *task) { CommonConnectContext *ctx; ctx = g_task_get_task_data (task); g_assert (mm_bearer_ip_config_get_method (ctx->ip_config) != MM_BEARER_IP_METHOD_UNKNOWN); g_task_return_pointer (task, mm_bearer_connect_result_new (ctx->data, ctx->ip_config, NULL), (GDestroyNotify) mm_bearer_connect_result_unref); g_object_unref (task); } static void cgcontrdp_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; CommonConnectContext *ctx; gchar *local_address = NULL; gchar *subnet = NULL; gchar *dns_addresses[3] = { NULL, NULL, NULL }; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response || !mm_3gpp_parse_cgcontrdp_response (response, NULL, /* cid */ NULL, /* bearer id */ NULL, /* apn */ &local_address, &subnet, NULL, /* gateway_address */ &dns_addresses[0], &dns_addresses[1], &error)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "IPv4 address retrieved: %s", local_address); mm_bearer_ip_config_set_address (ctx->ip_config, local_address); mm_obj_dbg (self, "IPv4 subnet retrieved: %s", subnet); mm_bearer_ip_config_set_prefix (ctx->ip_config, mm_netmask_to_cidr (subnet)); if (dns_addresses[0]) mm_obj_dbg (self, "primary DNS retrieved: %s", dns_addresses[0]); if (dns_addresses[1]) mm_obj_dbg (self, "secondary DNS retrieved: %s", dns_addresses[1]); mm_bearer_ip_config_set_dns (ctx->ip_config, (const gchar **) dns_addresses); g_free (local_address); g_free (subnet); g_free (dns_addresses[0]); g_free (dns_addresses[1]); mm_obj_dbg (self, "finished IP settings retrieval for PDP context #%u...", ctx->cid); complete_get_ip_config_3gpp (task); } static void uipaddr_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; gchar *cmd; GError *error = NULL; CommonConnectContext *ctx; gchar *gw_ipv4_address = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response || !mm_ublox_parse_uipaddr_response (response, NULL, /* cid */ NULL, /* if_name */ &gw_ipv4_address, NULL, /* ipv4_subnet */ NULL, /* ipv6_global_address */ NULL, /* ipv6_link_local_address */ &error)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "IPv4 gateway address retrieved: %s", gw_ipv4_address); mm_bearer_ip_config_set_gateway (ctx->ip_config, gw_ipv4_address); g_free (gw_ipv4_address); cmd = g_strdup_printf ("+CGCONTRDP=%u", ctx->cid); mm_obj_dbg (self, "gathering IP and DNS information for PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, 10, FALSE, (GAsyncReadyCallback) cgcontrdp_ready, task); g_free (cmd); } static void get_ip_config_3gpp (MMBroadbandBearer *_self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, MMBearerIpFamily ip_family, GAsyncReadyCallback callback, gpointer user_data) { MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (_self); GTask *task; CommonConnectContext *ctx; if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self), MM_BROADBAND_MODEM (modem), primary, cid, data, NULL, callback, user_data))) return; ctx = g_task_get_task_data (task); ctx->ip_config = mm_bearer_ip_config_new (); /* If we're in BRIDGE mode, we need to ask for static IP addressing details: * - AT+UIPADDR=[CID] will give us the default gateway address. * - +CGCONTRDP?[CID] will give us the IP address, subnet and DNS addresses. */ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_BRIDGE) { gchar *cmd; mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_STATIC); cmd = g_strdup_printf ("+UIPADDR=%u", cid); mm_obj_dbg (self, "gathering gateway information for PDP context #%u...", cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, 10, FALSE, (GAsyncReadyCallback) uipaddr_ready, task); g_free (cmd); return; } /* If we're in ROUTER networking mode, we just need to request DHCP on the * network interface. Early return with that result. */ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_ROUTER) { mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_DHCP); complete_get_ip_config_3gpp (task); return; } g_assert_not_reached (); } /*****************************************************************************/ /* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ static MMPort * dial_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); } static void cedata_activate_ready (MMBaseModem *modem, GAsyncResult *res, MMBroadbandBearerUblox *self) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { mm_obj_warn (self, "ECM data connection attempt failed: %s", error->message); mm_base_bearer_report_connection_status (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_DISCONNECTED); g_error_free (error); } /* we received a full bearer object reference */ g_object_unref (self); } static void cgact_activate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; CommonConnectContext *ctx; ctx = g_task_get_task_data (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) g_task_return_error (task, error); else g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); } static void activate_3gpp (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; g_autofree gchar *cmd = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* SARA-U2xx and LISA-U20x only expose one CDC-ECM interface. Hence, * the fixed 0 as the interface index here. When we see modems with * multiple interfaces, this needs to be revisited. */ if (self->priv->profile == MM_UBLOX_USB_PROFILE_ECM && self->priv->cedata == FEATURE_SUPPORTED) { cmd = g_strdup_printf ("+UCEDATA=%u,0", ctx->cid); mm_obj_dbg (self, "establishing ECM data connection for PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), cmd, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback) cedata_activate_ready, g_object_ref (self)); /* We'll mark the task done here since the modem expects the DHCP discover packet while +UCEDATA runs. If the command fails, we'll mark the bearer disconnected later in the callback. */ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); g_object_unref (task); return; } cmd = g_strdup_printf ("+CGACT=1,%u", ctx->cid); mm_obj_dbg (self, "activating PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), cmd, MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback) cgact_activate_ready, task); } static void test_cedata_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; self = g_task_get_source_object (task); response = mm_base_modem_at_command_finish (modem, res, NULL); if (response) self->priv->cedata = FEATURE_SUPPORTED; else self->priv->cedata = FEATURE_UNSUPPORTED; mm_obj_dbg (self, "+UCEDATA command%s available", (self->priv->cedata == FEATURE_SUPPORTED) ? "" : " not"); activate_3gpp (task); } static void test_cedata (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); /* We don't need to test for +UCEDATA if we're not using CDC-ECM or if we have tested before. Instead, we jump right to the activation. */ if (self->priv->profile != MM_UBLOX_USB_PROFILE_ECM || self->priv->cedata != FEATURE_SUPPORT_UNKNOWN) { activate_3gpp (task); return; } mm_obj_dbg (self, "checking availability of +UCEDATA command..."); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), "+UCEDATA=?", 3, TRUE, /* allow_cached */ (GAsyncReadyCallback) test_cedata_ready, task); } static void uauthreq_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { const gchar *response; GError *error = NULL; response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { CommonConnectContext *ctx; ctx = g_task_get_task_data (task); /* If authentication required and the +UAUTHREQ failed, abort */ if (ctx->auth_required) { g_task_return_error (task, error); g_object_unref (task); return; } /* Otherwise, ignore */ g_error_free (error); } test_cedata (task); } static void authenticate_3gpp (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; g_autofree gchar *cmd = NULL; MMBearerAllowedAuth allowed_auth; gint ublox_auth = -1; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); if (!ctx->auth_required) { mm_obj_dbg (self, "not using authentication"); ublox_auth = 0; goto out; } if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN || allowed_auth == (MM_BEARER_ALLOWED_AUTH_PAP | MM_BEARER_ALLOWED_AUTH_CHAP)) { mm_obj_dbg (self, "using automatic authentication method"); if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO) ublox_auth = 3; else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP) ublox_auth = 2; else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_PAP) ublox_auth = 1; else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_NONE) ublox_auth = 0; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) { mm_obj_dbg (self, "using PAP authentication method"); ublox_auth = 1; } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) { mm_obj_dbg (self, "using CHAP authentication method"); ublox_auth = 2; } out: if (ublox_auth < 0) { g_autofree gchar *str = NULL; str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth); g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Cannot use any of the specified authentication methods (%s)", str); g_object_unref (task); return; } if (ublox_auth > 0) { const gchar *user; const gchar *password; g_autofree gchar *quoted_user = NULL; g_autofree gchar *quoted_password = NULL; user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); quoted_user = mm_port_serial_at_quote_string (user); quoted_password = mm_port_serial_at_quote_string (password); cmd = g_strdup_printf ("+UAUTHREQ=%u,%u,%s,%s", ctx->cid, ublox_auth, quoted_user, quoted_password); } else cmd = g_strdup_printf ("+UAUTHREQ=%u,0,\"\",\"\"", ctx->cid); mm_obj_dbg (self, "setting up authentication preferences in PDP context #%u...", ctx->cid); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), cmd, 10, FALSE, (GAsyncReadyCallback) uauthreq_ready, task); } static void uauthreq_test_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; self = g_task_get_source_object (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) goto out; self->priv->allowed_auths = mm_ublox_parse_uauthreq_test (response, self, &error); out: if (error) { CommonConnectContext *ctx; ctx = g_task_get_task_data (task); /* If authentication required and the +UAUTHREQ test failed, abort */ if (ctx->auth_required) { g_task_return_error (task, error); g_object_unref (task); return; } /* Otherwise, ignore and jump to test_cedata directly as no auth setup * is needed */ g_error_free (error); test_cedata (task); return; } authenticate_3gpp (task); } static void check_supported_authentication_methods (GTask *task) { MMBroadbandBearerUblox *self; CommonConnectContext *ctx; const gchar *user; const gchar *password; MMBearerAllowedAuth allowed_auth; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); /* Flag whether authentication is required. If it isn't, we won't fail * connection attempt if the +UAUTHREQ command fails */ ctx->auth_required = (user && password && allowed_auth != MM_BEARER_ALLOWED_AUTH_NONE); /* If we already cached the support, not do it again */ if (self->priv->allowed_auths != MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN) { authenticate_3gpp (task); return; } mm_obj_dbg (self, "checking supported authentication methods..."); mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem), "+UAUTHREQ=?", 10, TRUE, /* allow cached */ (GAsyncReadyCallback) uauthreq_test_ready, task); } static void dial_3gpp (MMBroadbandBearer *self, MMBaseModem *modem, MMPortSerialAt *primary, guint cid, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self), MM_BROADBAND_MODEM (modem), primary, cid, NULL, /* data, unused */ cancellable, callback, user_data))) return; check_supported_authentication_methods (task); } /*****************************************************************************/ /* 3GPP disconnection */ static gboolean disconnect_3gpp_finish (MMBroadbandBearer *self, GAsyncResult *res, GError **error) { return g_task_propagate_boolean (G_TASK (res), error); } static void cgact_deactivate_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; self = g_task_get_source_object (task); response = mm_base_modem_at_command_finish (modem, res, &error); if (!response) { /* TOBY-L4 and TOBY-L2 L2 don't allow to disconnect the last LTE bearer * as that would imply de-registration from the LTE network, so we just * assume that it's disconnected from the user point of view. * * TOBY-L4 reports this as a generic unknown Packet Domain Error, which * is a bit unfortunate: * AT+CGACT=0,1 * +CME ERROR: 148 * * TOBY-L2 reports this as "LAST PDN disconnection not allowed" but using * the legacy numeric value before 3GPP Rel 11 (i.e. 151 instead of 171). * AT+CGACT=0,1 * +CME ERROR: 151 */ if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN) && !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED) && !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY)) { g_task_return_error (task, error); g_object_unref (task); return; } mm_obj_dbg (self, "ignored error when disconnecting last LTE bearer: %s", error->message); g_clear_error (&error); } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void disconnect_3gpp (MMBroadbandBearer *self, MMBroadbandModem *modem, MMPortSerialAt *primary, MMPortSerialAt *secondary, MMPort *data, guint cid, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_autofree gchar *cmd = NULL; if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self), MM_BROADBAND_MODEM (modem), primary, cid, data, NULL, callback, user_data))) return; cmd = g_strdup_printf ("+CGACT=0,%u", cid); mm_obj_dbg (self, "deactivating PDP context #%u...", cid); mm_base_modem_at_command (MM_BASE_MODEM (modem), cmd, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, FALSE, (GAsyncReadyCallback) cgact_deactivate_ready, task); } /*****************************************************************************/ /* Reload statistics */ typedef struct { guint64 bytes_rx; guint64 bytes_tx; } StatsResult; static gboolean reload_stats_finish (MMBaseBearer *self, guint64 *bytes_rx, guint64 *bytes_tx, GAsyncResult *res, GError **error) { StatsResult *result; result = g_task_propagate_pointer (G_TASK (res), error); if (!result) return FALSE; if (bytes_rx) *bytes_rx = result->bytes_rx; if (bytes_tx) *bytes_tx = result->bytes_tx; g_free (result); return TRUE; } static void ugcntrd_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; const gchar *response; GError *error = NULL; guint64 tx_bytes = 0; guint64 rx_bytes = 0; guint cid; self = MM_BROADBAND_BEARER_UBLOX (g_task_get_source_object (task)); cid = mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (self)); response = mm_base_modem_at_command_finish (modem, res, &error); if (response) mm_ublox_parse_ugcntrd_response_for_cid (response, cid, &tx_bytes, &rx_bytes, NULL, NULL, &error); if (error) { g_prefix_error (&error, "Couldn't load PDP context %u statistics: ", cid); g_task_return_error (task, error); } else { StatsResult *result; result = g_new (StatsResult, 1); result->bytes_rx = rx_bytes; result->bytes_tx = tx_bytes; g_task_return_pointer (task, result, g_free); } g_object_unref (task); } static void run_reload_stats (MMBroadbandBearerUblox *self, GTask *task) { /* Unsupported? */ if (self->priv->statistics == FEATURE_UNSUPPORTED) { g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Loading statistics isn't supported by this device"); g_object_unref (task); return; } /* Supported */ if (self->priv->statistics == FEATURE_SUPPORTED) { MMBaseModem *modem = NULL; g_object_get (MM_BASE_BEARER (self), MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command (MM_BASE_MODEM (modem), "+UGCNTRD", 3, FALSE, (GAsyncReadyCallback) ugcntrd_ready, task); g_object_unref (modem); return; } g_assert_not_reached (); } static void ugcntrd_test_ready (MMBaseModem *modem, GAsyncResult *res, GTask *task) { MMBroadbandBearerUblox *self; self = MM_BROADBAND_BEARER_UBLOX (g_task_get_source_object (task)); if (!mm_base_modem_at_command_finish (modem, res, NULL)) self->priv->statistics = FEATURE_UNSUPPORTED; else self->priv->statistics = FEATURE_SUPPORTED; run_reload_stats (self, task); } static void reload_stats (MMBaseBearer *self, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (self, NULL, callback, user_data); if (MM_BROADBAND_BEARER_UBLOX (self)->priv->statistics == FEATURE_SUPPORT_UNKNOWN) { MMBaseModem *modem = NULL; g_object_get (MM_BASE_BEARER (self), MM_BASE_BEARER_MODEM, &modem, NULL); mm_base_modem_at_command (MM_BASE_MODEM (modem), "+UGCNTRD=?", 3, FALSE, (GAsyncReadyCallback) ugcntrd_test_ready, task); g_object_unref (modem); return; } run_reload_stats (MM_BROADBAND_BEARER_UBLOX (self), task); } /*****************************************************************************/ MMBaseBearer * mm_broadband_bearer_ublox_new_finish (GAsyncResult *res, GError **error) { GObject *source; GObject *bearer; source = g_async_result_get_source_object (res); bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); g_object_unref (source); if (!bearer) return NULL; /* Only export valid bearers */ mm_base_bearer_export (MM_BASE_BEARER (bearer)); return MM_BASE_BEARER (bearer); } void mm_broadband_bearer_ublox_new (MMBroadbandModem *modem, MMUbloxUsbProfile profile, MMUbloxNetworkingMode mode, MMBearerProperties *config, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_assert (mode == MM_UBLOX_NETWORKING_MODE_ROUTER || mode == MM_UBLOX_NETWORKING_MODE_BRIDGE); g_async_initable_new_async ( MM_TYPE_BROADBAND_BEARER_UBLOX, G_PRIORITY_DEFAULT, cancellable, callback, user_data, MM_BASE_BEARER_MODEM, modem, MM_BASE_BEARER_CONFIG, config, MM_BROADBAND_BEARER_UBLOX_USB_PROFILE, profile, MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE, mode, NULL); } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (object); switch (prop_id) { case PROP_USB_PROFILE: self->priv->profile = g_value_get_enum (value); break; case PROP_NETWORKING_MODE: self->priv->mode = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (object); switch (prop_id) { case PROP_USB_PROFILE: g_value_set_enum (value, self->priv->profile); break; case PROP_NETWORKING_MODE: g_value_set_enum (value, self->priv->mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void mm_broadband_bearer_ublox_init (MMBroadbandBearerUblox *self) { /* Initialize private data */ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUbloxPrivate); /* Defaults */ self->priv->profile = MM_UBLOX_USB_PROFILE_UNKNOWN; self->priv->mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN; self->priv->allowed_auths = MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN; self->priv->statistics = FEATURE_SUPPORT_UNKNOWN; self->priv->cedata = FEATURE_SUPPORT_UNKNOWN; } static void mm_broadband_bearer_ublox_class_init (MMBroadbandBearerUbloxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass); MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); g_type_class_add_private (object_class, sizeof (MMBroadbandBearerUbloxPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; /* Note: the ublox plugin uses the generic AT+CGACT? based check to monitor * the connection status (i.e. default load_connection_status()) */ base_bearer_class->reload_stats = reload_stats; base_bearer_class->reload_stats_finish = reload_stats_finish; broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; broadband_bearer_class->dial_3gpp = dial_3gpp; broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish; broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp; broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish; properties[PROP_USB_PROFILE] = g_param_spec_enum (MM_BROADBAND_BEARER_UBLOX_USB_PROFILE, "USB profile", "USB profile in use", MM_TYPE_UBLOX_USB_PROFILE, MM_UBLOX_USB_PROFILE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_USB_PROFILE, properties[PROP_USB_PROFILE]); properties[PROP_NETWORKING_MODE] = g_param_spec_enum (MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE, "Networking mode", "Networking mode in use", MM_TYPE_UBLOX_NETWORKING_MODE, MM_UBLOX_NETWORKING_MODE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_NETWORKING_MODE, properties[PROP_NETWORKING_MODE]); }