// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Aleksander Morgado */ #include "nm-default.h" #include "nm-modem-broadband.h" #include "nm-service-providers.h" #include #include #include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" #include "platform/nm-platform.h" #include "nm-ip4-config.h" #include "nm-ip6-config.h" #define NM_MODEM_BROADBAND_MODEM "modem" #define MODEM_CAPS_3GPP(caps) (caps & (MM_MODEM_CAPABILITY_GSM_UMTS | \ MM_MODEM_CAPABILITY_LTE | \ MM_MODEM_CAPABILITY_LTE_ADVANCED)) #define MODEM_CAPS_3GPP2(caps) (caps & (MM_MODEM_CAPABILITY_CDMA_EVDO)) /* Maximum time to keep the DBus call waiting for a connection result */ #define MODEM_CONNECT_TIMEOUT_SECS 120 /*****************************************************************************/ typedef enum { CONNECT_STEP_FIRST, CONNECT_STEP_WAIT_FOR_SIM, CONNECT_STEP_UNLOCK, CONNECT_STEP_WAIT_FOR_READY, CONNECT_STEP_CONNECT, CONNECT_STEP_LAST, } ConnectStep; typedef struct { NMModemBroadband *self; ConnectStep step; MMModemCapability caps; NMConnection *connection; GCancellable *cancellable; MMSimpleConnectProperties *connect_properties; GArray *ip_types; guint ip_types_i; guint ip_type_tries; GError *first_error; } ConnectContext; /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( PROP_MODEM, ); typedef struct { /* The modem object from dbus */ MMObject *modem_object; /* Per-interface objects */ MMModem *modem_iface; MMModem3gpp *modem_3gpp_iface; MMModemSimple *simple_iface; MMSim *sim_iface; /* Connection setup */ ConnectContext *ctx; MMBearer *bearer; MMBearerIpConfig *ipv4_config; MMBearerIpConfig *ipv6_config; guint idle_id_ip4; guint idle_id_ip6; guint32 pin_tries; } NMModemBroadbandPrivate; struct _NMModemBroadband { NMModem parent; NMModemBroadbandPrivate _priv; }; struct _NMModemBroadbandClass { NMModemClass parent; }; G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM) #define NM_MODEM_BROADBAND_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMModemBroadband, NM_IS_MODEM_BROADBAND) /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_MB #define _NMLOG_PREFIX_NAME "modem-broadband" #define _NMLOG(level, ...) \ G_STMT_START { \ const NMLogLevel _level = (level); \ \ if (nm_logging_enabled (_level, (_NMLOG_DOMAIN))) { \ NMModemBroadband *const __self = (self); \ char __prefix_name[128]; \ const char *__uid; \ \ _nm_log (_level, (_NMLOG_DOMAIN), 0, NULL, \ ((__self && __self->_priv.ctx) \ ? nm_connection_get_uuid (__self->_priv.ctx->connection) \ : NULL), \ "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ (__self \ ? ({ \ ((__uid = nm_modem_get_uid ((NMModem *) __self)) \ ? nm_sprintf_buf (__prefix_name, "[%s]", __uid) \ : "(null)"); \ }) \ : "") \ _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ } \ } G_STMT_END /*****************************************************************************/ static NMDeviceStateReason translate_mm_error (NMModemBroadband *self, GError *error) { NMDeviceStateReason reason; g_return_val_if_fail (error != NULL, NM_DEVICE_STATE_REASON_UNKNOWN); if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER)) reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER; else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_DIALTONE)) reason = NM_DEVICE_STATE_REASON_MODEM_NO_DIAL_TONE; else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_BUSY)) reason = NM_DEVICE_STATE_REASON_MODEM_BUSY; else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_ANSWER)) reason = NM_DEVICE_STATE_REASON_MODEM_DIAL_TIMEOUT; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED)) reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT)) reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_TIMEOUT; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK)) reason = NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED)) reason = NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)) reason = NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) reason = NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) reason = NM_DEVICE_STATE_REASON_GSM_SIM_WRONG; else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD)) reason = NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT; else { /* unable to map the ModemManager error to a NM_DEVICE_STATE_REASON */ _LOGD ("unmapped error detected: '%s'", error->message); reason = NM_DEVICE_STATE_REASON_UNKNOWN; } return reason; } /*****************************************************************************/ static void get_capabilities (NMModem *_self, NMDeviceModemCapabilities *modem_caps, NMDeviceModemCapabilities *current_caps) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); MMModemCapability all_supported = MM_MODEM_CAPABILITY_NONE; MMModemCapability *supported; guint n_supported; /* For now, we don't care about the capability combinations, just merge all * combinations in a single mask */ if (mm_modem_get_supported_capabilities (self->_priv.modem_iface, &supported, &n_supported)) { guint i; for (i = 0; i < n_supported; i++) all_supported |= supported[i]; g_free (supported); } *modem_caps = (NMDeviceModemCapabilities) all_supported; *current_caps = (NMDeviceModemCapabilities) mm_modem_get_current_capabilities (self->_priv.modem_iface); } static gboolean owns_port (NMModem *_self, const char *iface) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); const MMModemPortInfo *ports = NULL; guint n_ports = 0, i; mm_modem_peek_ports (self->_priv.modem_iface, &ports, &n_ports); for (i = 0; i < n_ports; i++) { if (nm_streq0 (iface, ports[i].name)) return TRUE; } return FALSE; } /*****************************************************************************/ static void ask_for_pin (NMModemBroadband *self) { guint32 tries; tries = self->_priv.pin_tries++; nm_modem_get_secrets (NM_MODEM (self), NM_SETTING_GSM_SETTING_NAME, tries ? TRUE : FALSE, NM_SETTING_GSM_PIN); } static NMModemIPMethod get_bearer_ip_method (MMBearerIpConfig *config) { MMBearerIpMethod mm_method; mm_method = mm_bearer_ip_config_get_method (config); if (mm_method == MM_BEARER_IP_METHOD_PPP) return NM_MODEM_IP_METHOD_PPP; else if (mm_method == MM_BEARER_IP_METHOD_STATIC) return NM_MODEM_IP_METHOD_STATIC; else if (mm_method == MM_BEARER_IP_METHOD_DHCP) return NM_MODEM_IP_METHOD_AUTO; return NM_MODEM_IP_METHOD_UNKNOWN; } static MMSimpleConnectProperties * create_cdma_connect_properties (NMConnection *connection) { MMSimpleConnectProperties *properties; properties = mm_simple_connect_properties_new (); #if !MM_CHECK_VERSION (1, 9, 1) { NMSettingCdma *setting; const char *str; setting = nm_connection_get_setting_cdma (connection); str = nm_setting_cdma_get_number (setting); if (str) mm_simple_connect_properties_set_number (properties, str); } #endif return properties; } static MMSimpleConnectProperties * create_gsm_connect_properties (NMConnection *connection, const char *apn, const char *username, const char *password) { NMSettingGsm *setting; NMSettingPpp *s_ppp; MMSimpleConnectProperties *properties; const char *str; setting = nm_connection_get_setting_gsm (connection); properties = mm_simple_connect_properties_new (); mm_simple_connect_properties_set_apn (properties, apn ?: ""); if (username) mm_simple_connect_properties_set_user (properties, username); if (password) mm_simple_connect_properties_set_password (properties, password); str = nm_setting_gsm_get_network_id (setting); if (str) mm_simple_connect_properties_set_operator_id (properties, str); str = nm_setting_gsm_get_pin (setting); if (str) mm_simple_connect_properties_set_pin (properties, str); /* Roaming */ if (nm_setting_gsm_get_home_only (setting)) mm_simple_connect_properties_set_allow_roaming (properties, FALSE); /* For IpMethod == STATIC or DHCP */ s_ppp = nm_connection_get_setting_ppp (connection); if (s_ppp) { MMBearerAllowedAuth allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN; if (nm_setting_ppp_get_noauth (s_ppp)) allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE; if (!nm_setting_ppp_get_refuse_pap (s_ppp)) allowed_auth |= MM_BEARER_ALLOWED_AUTH_PAP; if (!nm_setting_ppp_get_refuse_chap (s_ppp)) allowed_auth |= MM_BEARER_ALLOWED_AUTH_CHAP; if (!nm_setting_ppp_get_refuse_mschap (s_ppp)) allowed_auth |= MM_BEARER_ALLOWED_AUTH_MSCHAP; if (!nm_setting_ppp_get_refuse_mschapv2 (s_ppp)) allowed_auth |= MM_BEARER_ALLOWED_AUTH_MSCHAPV2; if (!nm_setting_ppp_get_refuse_eap (s_ppp)) allowed_auth |= MM_BEARER_ALLOWED_AUTH_EAP; mm_simple_connect_properties_set_allowed_auth (properties, allowed_auth); } return properties; } static void connect_context_clear (NMModemBroadband *self) { if (self->_priv.ctx) { ConnectContext *ctx = self->_priv.ctx; g_clear_error (&ctx->first_error); g_clear_pointer (&ctx->ip_types, g_array_unref); nm_clear_g_cancellable (&ctx->cancellable); g_clear_object (&ctx->connection); g_clear_object (&ctx->connect_properties); g_clear_object (&ctx->self); g_slice_free (ConnectContext, ctx); self->_priv.ctx = NULL; } } static void connect_context_step (NMModemBroadband *self); static void connect_ready (MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *self) { ConnectContext *ctx; GError *error = NULL; NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; MMBearer *bearer; bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } ctx = self->_priv.ctx; if (!ctx) return; self->_priv.bearer = bearer; if (!self->_priv.bearer) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) && mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN)) { g_error_free (error); /* Request PIN */ ask_for_pin (self); connect_context_clear (self); return; } /* Save the error, if it's the first one */ if (!ctx->first_error) { /* Strip remote error info before saving it */ if (g_dbus_error_is_remote_error (error)) g_dbus_error_strip_remote_error (error); ctx->first_error = error; } else g_clear_error (&error); if ( ctx->ip_type_tries == 0 && g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) { /* Try one more time */ ctx->ip_type_tries++; } else { /* If the modem/provider lies and the IP type we tried isn't supported, * retry with the next one, if any. */ ctx->ip_types_i++; ctx->ip_type_tries = 0; } connect_context_step (self); return; } /* Grab IP configurations */ self->_priv.ipv4_config = mm_bearer_get_ipv4_config (self->_priv.bearer); if (self->_priv.ipv4_config) ip4_method = get_bearer_ip_method (self->_priv.ipv4_config); self->_priv.ipv6_config = mm_bearer_get_ipv6_config (self->_priv.bearer); if (self->_priv.ipv6_config) ip6_method = get_bearer_ip_method (self->_priv.ipv6_config); if (!nm_modem_set_data_port (NM_MODEM (self), NM_PLATFORM_GET, mm_bearer_get_interface (self->_priv.bearer), ip4_method, ip6_method, mm_bearer_get_ip_timeout (self->_priv.bearer), &error)) { _LOGW ("failed to connect modem: %s", error->message); g_error_free (error); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED); connect_context_clear (self); return; } ctx->step++; connect_context_step (self); } static void send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self) { gs_free_error GError *error = NULL; mm_sim_send_pin_finish (sim, result, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; if (!self->_priv.ctx || self->_priv.ctx->step != CONNECT_STEP_UNLOCK) g_return_if_reached (); if (error) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) && mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN)) ask_for_pin (self); else nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, translate_mm_error (self, error)); return; } self->_priv.ctx->step++; connect_context_step (self); } static void find_gsm_apn_cb (const char *apn, const char *username, const char *password, const char *gateway, const char *auth_method, const GSList *dns, GError *error, gpointer user_data) { NMModemBroadband *self = user_data; NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); ConnectContext *ctx = priv->ctx; if (error) { _LOGW ("failed to connect '%s': APN not found: %s", nm_connection_get_id (ctx->connection), error->message); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_GSM_APN_FAILED); connect_context_clear (self); return; } /* Blank APN ("") means the default subscription APN */ ctx->connect_properties = create_gsm_connect_properties (ctx->connection, apn, username, password); g_return_if_fail (ctx->connect_properties); connect_context_step (self); } static gboolean try_create_connect_properties (NMModemBroadband *self) { NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); ConnectContext *ctx = priv->ctx; if (MODEM_CAPS_3GPP (ctx->caps)) { NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection); if (!s_gsm || nm_setting_gsm_get_auto_config (s_gsm)) { gs_unref_object MMModem3gpp *modem_3gpp = NULL; const char *network_id = NULL; s_gsm = nm_connection_get_setting_gsm (ctx->connection); if (s_gsm) network_id = nm_setting_gsm_get_network_id (s_gsm); if (!network_id) { if (mm_modem_get_state (self->_priv.modem_iface) < MM_MODEM_STATE_REGISTERED) return FALSE; modem_3gpp = mm_object_get_modem_3gpp (priv->modem_object); network_id = mm_modem_3gpp_get_operator_code (modem_3gpp); } if (!network_id) { _LOGW ("failed to connect '%s': unable to determine the network id", nm_connection_get_id (ctx->connection)); goto out; } nm_service_providers_find_gsm_apn (MOBILE_BROADBAND_PROVIDER_INFO_DATABASE, network_id, ctx->cancellable, find_gsm_apn_cb, self); } else { ctx->connect_properties = create_gsm_connect_properties (ctx->connection, nm_setting_gsm_get_apn (s_gsm), nm_setting_gsm_get_username (s_gsm), nm_setting_gsm_get_password (s_gsm)); g_return_val_if_fail (ctx->connect_properties, TRUE); } return TRUE; } else if (MODEM_CAPS_3GPP2 (ctx->caps)) { ctx->connect_properties = create_cdma_connect_properties (ctx->connection); g_return_val_if_fail (ctx->connect_properties, FALSE); return TRUE; } else { _LOGW ("failed to connect '%s': not a mobile broadband modem", nm_connection_get_id (ctx->connection)); } out: nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); connect_context_clear (self); return TRUE; } static void connect_context_step (NMModemBroadband *self) { ConnectContext *ctx = self->_priv.ctx; switch (ctx->step) { case CONNECT_STEP_FIRST: ctx->step++; /* fall through */ case CONNECT_STEP_WAIT_FOR_SIM: if (MODEM_CAPS_3GPP (ctx->caps) && !self->_priv.sim_iface) { /* Have to wait for the SIM to show up */ break; } ctx->step++; /* fall through */ case CONNECT_STEP_UNLOCK: if ( MODEM_CAPS_3GPP (ctx->caps) && mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN) { NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection); const char *pin = nm_setting_gsm_get_pin (s_gsm); /* If we have a PIN already, send it. If we don't, get it. */ if (pin) { mm_sim_send_pin (self->_priv.sim_iface, pin, ctx->cancellable, (GAsyncReadyCallback) send_pin_ready, self); } else { ask_for_pin (self); } break; } ctx->step++; /* fall through */ case CONNECT_STEP_WAIT_FOR_READY: { GError *error = NULL; if (mm_modem_get_state (self->_priv.modem_iface) <= MM_MODEM_STATE_LOCKED) break; if (!try_create_connect_properties (self)) break; if (!self->_priv.ctx) break; /* Build up list of IP types that we need to use in the retries */ ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error); if (!ctx->ip_types) { _LOGW ("failed to connect '%s': %s", nm_connection_get_id (ctx->connection), error->message); g_clear_error (&error); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); connect_context_clear (self); break; } ctx->step++; } /* fall through */ case CONNECT_STEP_CONNECT: if (!ctx->connect_properties) break; if (ctx->ip_types_i < ctx->ip_types->len) { NMModemIPType current; current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i); if (current == NM_MODEM_IP_TYPE_IPV4) mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4); else if (current == NM_MODEM_IP_TYPE_IPV6) mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6); else if (current == NM_MODEM_IP_TYPE_IPV4V6) mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6); else g_return_if_reached (); _nm_modem_set_apn (NM_MODEM (self), mm_simple_connect_properties_get_apn (ctx->connect_properties)); _LOGD ("launching connection with ip type '%s' (try %d)", nm_modem_ip_type_to_string (current), ctx->ip_type_tries + 1); mm_modem_simple_connect (self->_priv.simple_iface, ctx->connect_properties, ctx->cancellable, (GAsyncReadyCallback) connect_ready, self); break; } ctx->step++; /* fall through */ case CONNECT_STEP_LAST: if (self->_priv.ipv4_config || self->_priv.ipv6_config) nm_modem_emit_prepare_result (NM_MODEM (self), TRUE, NM_DEVICE_STATE_REASON_NONE); else { /* If we have a saved error from a previous attempt, use it */ if (!ctx->first_error) ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "invalid bearer IP configuration"); _LOGW ("failed to connect modem: %s", ctx->first_error->message); nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, translate_mm_error (self, ctx->first_error)); } connect_context_clear (self); break; } } static NMActStageReturn modem_act_stage1_prepare (NMModem *_self, NMConnection *connection, NMDeviceStateReason *out_failure_reason) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); /* Make sure we can get the Simple interface from the modem */ if (!self->_priv.simple_iface) { self->_priv.simple_iface = mm_object_get_modem_simple (self->_priv.modem_object); if (!self->_priv.simple_iface) { _LOGW ("cannot access the Simple mobile broadband modem interface"); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); return NM_ACT_STAGE_RETURN_FAILURE; } } connect_context_clear (self); /* Allocate new context for this connect stage attempt */ self->_priv.ctx = g_slice_new0 (ConnectContext); self->_priv.ctx->caps = mm_modem_get_current_capabilities (self->_priv.modem_iface); self->_priv.ctx->cancellable = g_cancellable_new (); self->_priv.ctx->connection = g_object_ref (connection); g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->_priv.simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000); connect_context_step (self); return NM_ACT_STAGE_RETURN_POSTPONE; } /*****************************************************************************/ static gboolean check_connection_compatible_with_modem (NMModem *_self, NMConnection *connection, GError **error) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); MMModemCapability modem_caps; modem_caps = mm_modem_get_current_capabilities (self->_priv.modem_iface); if (MODEM_CAPS_3GPP (modem_caps)) { if (!_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, error)) return FALSE; return TRUE; } if (MODEM_CAPS_3GPP2 (modem_caps)) { if (!_nm_connection_check_main_setting (connection, NM_SETTING_CDMA_SETTING_NAME, error)) return FALSE; return TRUE; } if ( !_nm_connection_check_main_setting (connection, NM_SETTING_GSM_SETTING_NAME, NULL) && !_nm_connection_check_main_setting (connection, NM_SETTING_CDMA_SETTING_NAME, NULL)) { nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE, "connection type %s is not supported by modem", nm_connection_get_connection_type (connection)); return FALSE; } nm_utils_error_set (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "modem lacks capabilities for %s profile", nm_connection_get_connection_type (connection)); return FALSE; } /*****************************************************************************/ static gboolean complete_connection (NMModem *modem, const char *iface, NMConnection *connection, NMConnection *const*existing_connections, GError **error) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); MMModemCapability modem_caps; NMSettingPpp *s_ppp; modem_caps = mm_modem_get_current_capabilities (self->_priv.modem_iface); /* PPP settings common to 3GPP and 3GPP2 */ s_ppp = nm_connection_get_setting_ppp (connection); if (!s_ppp) { s_ppp = (NMSettingPpp *) nm_setting_ppp_new (); g_object_set (G_OBJECT (s_ppp), NM_SETTING_PPP_LCP_ECHO_FAILURE, 5, NM_SETTING_PPP_LCP_ECHO_INTERVAL, 30, NULL); nm_connection_add_setting (connection, NM_SETTING (s_ppp)); } if (MODEM_CAPS_3GPP (modem_caps)) { NMSettingGsm *s_gsm; s_gsm = nm_connection_get_setting_gsm (connection); if (!s_gsm) { s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); nm_connection_add_setting (connection, NM_SETTING (s_gsm)); g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_AUTO_CONFIG, TRUE, NULL); } if (!nm_setting_gsm_get_device_id (s_gsm)) { g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_DEVICE_ID, nm_modem_get_device_id (modem), NULL); } nm_utils_complete_generic (NM_PLATFORM_GET, connection, NM_SETTING_GSM_SETTING_NAME, existing_connections, NULL, _("GSM connection"), NULL, NULL, FALSE); /* No IPv6 yet by default */ return TRUE; } if (MODEM_CAPS_3GPP2 (modem_caps)) { NMSettingCdma *s_cdma; s_cdma = nm_connection_get_setting_cdma (connection); if (!s_cdma) { s_cdma = (NMSettingCdma *) nm_setting_cdma_new (); nm_connection_add_setting (connection, NM_SETTING (s_cdma)); } if (!nm_setting_cdma_get_number (s_cdma)) g_object_set (G_OBJECT (s_cdma), NM_SETTING_CDMA_NUMBER, "#777", NULL); nm_utils_complete_generic (NM_PLATFORM_GET, connection, NM_SETTING_CDMA_SETTING_NAME, existing_connections, NULL, _("CDMA connection"), NULL, iface, FALSE); /* No IPv6 yet by default */ return TRUE; } g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, "Device is not a mobile broadband modem"); return FALSE; } /*****************************************************************************/ static gboolean get_user_pass (NMModem *modem, NMConnection *connection, const char **user, const char **pass) { NMSettingGsm *s_gsm; NMSettingCdma *s_cdma; s_gsm = nm_connection_get_setting_gsm (connection); s_cdma = nm_connection_get_setting_cdma (connection); if (!s_gsm && !s_cdma) return FALSE; if (user) { if (s_gsm) *user = nm_setting_gsm_get_username (s_gsm); else if (s_cdma) *user = nm_setting_cdma_get_username (s_cdma); } if (pass) { if (s_gsm) *pass = nm_setting_gsm_get_password (s_gsm); else if (s_cdma) *pass = nm_setting_cdma_get_password (s_cdma); } return TRUE; } /*****************************************************************************/ /* Query/Update enabled state */ static void set_power_state_low_ready (MMModem *modem, GAsyncResult *result, NMModemBroadband *self) { GError *error = NULL; if (!mm_modem_set_power_state_finish (modem, result, &error)) { /* Log but ignore errors; not all modems support low power state */ _LOGD ("failed to set modem low power state: %s", NM_G_ERROR_MSG (error)); g_clear_error (&error); } /* Balance refcount */ g_object_unref (self); } static void modem_disable_ready (MMModem *modem_iface, GAsyncResult *res, NMModemBroadband *self) { GError *error = NULL; if (mm_modem_disable_finish (modem_iface, res, &error)) { /* Once disabled, move to low-power mode */ mm_modem_set_power_state (modem_iface, MM_MODEM_POWER_STATE_LOW, NULL, (GAsyncReadyCallback) set_power_state_low_ready, g_object_ref (self)); } else { _LOGW ("failed to disable modem: %s", NM_G_ERROR_MSG (error)); nm_modem_set_prev_state (NM_MODEM (self), "disable failed"); g_clear_error (&error); } /* Balance refcount */ g_object_unref (self); } static void modem_enable_ready (MMModem *modem_iface, GAsyncResult *res, NMModemBroadband *self) { GError *error = NULL; if (!mm_modem_enable_finish (modem_iface, res, &error)) { _LOGW ("failed to enable modem: %s", NM_G_ERROR_MSG (error)); nm_modem_set_prev_state (NM_MODEM (self), "enable failed"); g_clear_error (&error); } /* Balance refcount */ g_object_unref (self); } static void set_mm_enabled (NMModem *_self, gboolean enabled) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); if (enabled) { mm_modem_enable (self->_priv.modem_iface, NULL, /* cancellable */ (GAsyncReadyCallback)modem_enable_ready, g_object_ref (self)); } else { mm_modem_disable (self->_priv.modem_iface, NULL, /* cancellable */ (GAsyncReadyCallback)modem_disable_ready, g_object_ref (self)); } } /*****************************************************************************/ /* IPv4 method static */ static gboolean static_stage3_ip4_done (NMModemBroadband *self) { GError *error = NULL; gs_unref_object NMIP4Config *config = NULL; const char *data_port; const char *address_string; const char *gw_string; guint32 address_network; guint32 gw = 0; NMPlatformIP4Address address; const char **dns; guint i; guint32 ip4_route_table, ip4_route_metric; NMPlatformIP4Route *r; g_return_val_if_fail (self->_priv.ipv4_config, FALSE); g_return_val_if_fail (self->_priv.bearer, FALSE); self->_priv.idle_id_ip4 = 0; _LOGI ("IPv4 static configuration:"); /* Fully fail if invalid IP address retrieved */ address_string = mm_bearer_ip_config_get_address (self->_priv.ipv4_config); if ( !address_string || !nm_utils_parse_inaddr_bin (AF_INET, address_string, NULL, &address_network)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid address given %s%s%s", nm_modem_get_uid (NM_MODEM (self)), NM_PRINT_FMT_QUOTE_STRING (address_string)); goto out; } /* Missing gateway not a hard failure */ gw_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv4_config); if ( gw_string && !nm_utils_parse_inaddr_bin (AF_INET, gw_string, NULL, &gw)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid gateway address \"%s\"", nm_modem_get_uid (NM_MODEM (self)), gw_string); goto out; } data_port = mm_bearer_get_interface (self->_priv.bearer); g_return_val_if_fail (data_port, FALSE); config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); memset (&address, 0, sizeof (address)); address.address = address_network; address.peer_address = address_network; address.plen = mm_bearer_ip_config_get_prefix (self->_priv.ipv4_config); address.addr_source = NM_IP_CONFIG_SOURCE_WWAN; if (address.plen <= 32) nm_ip4_config_add_address (config, &address); _LOGI (" address %s/%d", address_string, address.plen); nm_modem_get_route_parameters (NM_MODEM (self), &ip4_route_table, &ip4_route_metric, NULL, NULL); r = &(NMPlatformIP4Route) { .rt_source = NM_IP_CONFIG_SOURCE_WWAN, .gateway = gw, .table_coerced = nm_platform_route_table_coerce (ip4_route_table), .metric = ip4_route_metric, }; nm_ip4_config_add_route (config, r, NULL); _LOGI (" gateway %s", gw_string); /* DNS servers */ dns = mm_bearer_ip_config_get_dns (self->_priv.ipv4_config); for (i = 0; dns && dns[i]; i++) { if ( nm_utils_parse_inaddr_bin (AF_INET, dns[i], NULL, &address_network) && address_network > 0) { nm_ip4_config_add_nameserver (config, address_network); _LOGI (" DNS %s", dns[i]); } } out: g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, config, error); g_clear_error (&error); return FALSE; } static NMActStageReturn static_stage3_ip4_config_start (NMModem *modem, NMActRequest *req, NMDeviceStateReason *out_failure_reason) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); /* We schedule it in an idle just to follow the same logic as in the * generic modem implementation. */ nm_clear_g_source (&priv->idle_id_ip4); priv->idle_id_ip4 = g_idle_add ((GSourceFunc) static_stage3_ip4_done, self); return NM_ACT_STAGE_RETURN_POSTPONE; } /*****************************************************************************/ /* IPv6 method static */ static gboolean stage3_ip6_done (NMModemBroadband *self) { GError *error = NULL; NMIP6Config *config = NULL; const char *data_port; const char *address_string; NMPlatformIP6Address address; NMModemIPMethod ip_method; const char **dns; guint i; g_return_val_if_fail (self->_priv.ipv6_config, FALSE); self->_priv.idle_id_ip6 = 0; memset (&address, 0, sizeof (address)); ip_method = get_bearer_ip_method (self->_priv.ipv6_config); address_string = mm_bearer_ip_config_get_address (self->_priv.ipv6_config); if (!address_string) { /* DHCP/SLAAC is allowed to skip addresses; other methods require it */ if (ip_method != NM_MODEM_IP_METHOD_AUTO) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: no address given", nm_modem_get_uid (NM_MODEM (self))); } goto out; } /* Fail if invalid IP address retrieved */ if (!inet_pton (AF_INET6, address_string, (void *) &(address.address))) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: invalid address given '%s'", nm_modem_get_uid (NM_MODEM (self)), address_string); goto out; } _LOGI ("IPv6 base configuration:"); data_port = mm_bearer_get_interface (self->_priv.bearer); g_return_val_if_fail (data_port, FALSE); config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); address.plen = mm_bearer_ip_config_get_prefix (self->_priv.ipv6_config); if (address.plen <= 128) nm_ip6_config_add_address (config, &address); _LOGI (" address %s/%d", address_string, address.plen); address_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv6_config); if (address_string) { guint32 ip6_route_table, ip6_route_metric; if (inet_pton (AF_INET6, address_string, &address.address) != 1) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: invalid gateway given '%s'", nm_modem_get_uid (NM_MODEM (self)), address_string); goto out; } nm_modem_get_route_parameters (NM_MODEM (self), NULL, NULL, &ip6_route_table, &ip6_route_metric); { const NMPlatformIP6Route r = { .rt_source = NM_IP_CONFIG_SOURCE_WWAN, .gateway = address.address, .table_coerced = nm_platform_route_table_coerce (ip6_route_table), .metric = ip6_route_metric, }; _LOGI (" gateway %s", address_string); nm_ip6_config_add_route (config, &r, NULL); } } else if (ip_method == NM_MODEM_IP_METHOD_STATIC) { /* Gateway required for the 'static' method */ error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IPv6 configuration failed: missing gateway", nm_modem_get_uid (NM_MODEM (self))); goto out; } /* DNS servers */ dns = mm_bearer_ip_config_get_dns (self->_priv.ipv6_config); for (i = 0; dns && dns[i]; i++) { struct in6_addr addr; if (inet_pton (AF_INET6, dns[i], &addr)) { nm_ip6_config_add_nameserver (config, &addr); _LOGI (" DNS %s", dns[i]); } } out: nm_modem_emit_ip6_config_result (NM_MODEM (self), config, error); g_clear_object (&config); g_clear_error (&error); return FALSE; } static NMActStageReturn stage3_ip6_config_request (NMModem *modem, NMDeviceStateReason *out_failure_reason) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); /* We schedule it in an idle just to follow the same logic as in the * generic modem implementation. */ nm_clear_g_source (&priv->idle_id_ip6); priv->idle_id_ip6 = g_idle_add ((GSourceFunc) stage3_ip6_done, self); return NM_ACT_STAGE_RETURN_POSTPONE; } /*****************************************************************************/ /* Disconnect */ typedef struct { NMModemBroadband *self; _NMModemDisconnectCallback callback; gpointer callback_user_data; GCancellable *cancellable; gboolean warn; } DisconnectContext; static void disconnect_context_complete (DisconnectContext *ctx, GError *error) { if (ctx->callback) ctx->callback (NM_MODEM (ctx->self), error, ctx->callback_user_data); nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->self); g_slice_free (DisconnectContext, ctx); } static void disconnect_context_complete_on_idle (gpointer user_data, GCancellable *cancellable) { DisconnectContext *ctx = user_data; gs_free_error GError *cancelled_error = NULL; g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); disconnect_context_complete (ctx, cancelled_error); } static void simple_disconnect_ready (GObject *source_object, GAsyncResult *res, gpointer user_data) { MMModemSimple *modem_iface = MM_MODEM_SIMPLE (source_object); DisconnectContext *ctx = user_data; gs_free_error GError *error = NULL; if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) { if ( ctx->warn && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { NMModemBroadband *self = ctx->self; _LOGW ("failed to disconnect modem: %s", error->message); } } disconnect_context_complete (ctx, error); } static void disconnect (NMModem *modem, gboolean warn, GCancellable *cancellable, _NMModemDisconnectCallback callback, gpointer user_data) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); DisconnectContext *ctx; connect_context_clear (self); _nm_modem_set_apn (NM_MODEM (self), NULL); ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); ctx->cancellable = nm_g_object_ref (cancellable); ctx->callback = callback; ctx->callback_user_data = user_data; /* Don't bother warning on FAILED since the modem is already gone */ ctx->warn = warn; /* Already cancelled or no simple-iface? We are done. */ if ( !ctx->self->_priv.simple_iface || g_cancellable_is_cancelled (cancellable)) { nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, ctx, cancellable); return; } _LOGD ("notifying ModemManager about the modem disconnection"); mm_modem_simple_disconnect (self->_priv.simple_iface, NULL, /* bearer path; if NULL given ALL get disconnected */ cancellable, simple_disconnect_ready, ctx); } /*****************************************************************************/ static void deactivate_cleanup (NMModem *modem, NMDevice *device, gboolean stop_ppp_manager) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); /* TODO: cancel SimpleConnect() if any */ /* Cleanup IPv4 addresses and routes */ g_clear_object (&self->_priv.ipv4_config); g_clear_object (&self->_priv.ipv6_config); g_clear_object (&self->_priv.bearer); self->_priv.pin_tries = 0; NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (modem, device, stop_ppp_manager); } /*****************************************************************************/ #define MAP_STATE(name) case MM_MODEM_STATE_##name: return NM_MODEM_STATE_##name; static NMModemState mm_state_to_nm (MMModemState mm_state) { switch (mm_state) { MAP_STATE(UNKNOWN) MAP_STATE(FAILED) MAP_STATE(INITIALIZING) MAP_STATE(LOCKED) MAP_STATE(DISABLED) MAP_STATE(DISABLING) MAP_STATE(ENABLING) MAP_STATE(ENABLED) MAP_STATE(SEARCHING) MAP_STATE(REGISTERED) MAP_STATE(DISCONNECTING) MAP_STATE(CONNECTING) MAP_STATE(CONNECTED) } return NM_MODEM_STATE_UNKNOWN; } static void modem_state_changed (MMModem *modem, MMModemState old_state, MMModemState new_state, MMModemStateChangeReason reason, NMModemBroadband *self) { /* After the SIM is unlocked MM1 will move the device to INITIALIZING which * is an unavailable state. That makes state handling confusing here, so * suppress this state change and let the modem move from LOCKED to DISABLED. */ if (new_state == MM_MODEM_STATE_INITIALIZING && old_state == MM_MODEM_STATE_LOCKED) return; nm_modem_set_state (NM_MODEM (self), mm_state_to_nm (new_state), mm_modem_state_change_reason_get_string (reason)); if (self->_priv.ctx && self->_priv.ctx->step == CONNECT_STEP_WAIT_FOR_READY) connect_context_step (self); } /*****************************************************************************/ static NMModemIPType mm_ip_family_to_nm (MMBearerIpFamily family) { NMModemIPType nm_type = NM_MODEM_IP_TYPE_UNKNOWN; if (family & MM_BEARER_IP_FAMILY_IPV4) nm_type |= NM_MODEM_IP_TYPE_IPV4; if (family & MM_BEARER_IP_FAMILY_IPV6) nm_type |= NM_MODEM_IP_TYPE_IPV6; if (family & MM_BEARER_IP_FAMILY_IPV4V6) nm_type |= MM_BEARER_IP_FAMILY_IPV4V6; return nm_type; } static void get_sim_ready (MMModem *modem, GAsyncResult *res, NMModemBroadband *self) { GError *error = NULL; MMSim *new_sim; new_sim = mm_modem_get_sim_finish (modem, res, &error); if (new_sim != self->_priv.sim_iface) { g_clear_object (&self->_priv.sim_iface); self->_priv.sim_iface = new_sim; } else g_clear_object (&new_sim); if (self->_priv.sim_iface) { g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, mm_sim_get_identifier (self->_priv.sim_iface), NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (self->_priv.sim_iface), NULL); /* If we're waiting for the SIM during a connect, proceed with the connect */ if (self->_priv.ctx && self->_priv.ctx->step == CONNECT_STEP_WAIT_FOR_SIM) connect_context_step (self); } else { _NMLOG (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND) ? LOGL_INFO : LOGL_WARN, "failed to retrieve SIM object: %s", NM_G_ERROR_MSG (error)); } g_clear_error (&error); g_object_unref (self); } static void sim_changed (MMModem *modem, GParamSpec *pspec, gpointer user_data) { NMModemBroadband *self = NM_MODEM_BROADBAND (user_data); g_return_if_fail (modem == self->_priv.modem_iface); if (mm_modem_get_sim_path (self->_priv.modem_iface)) { mm_modem_get_sim (self->_priv.modem_iface, NULL, /* cancellable */ (GAsyncReadyCallback) get_sim_ready, g_object_ref (self)); } else g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, NULL, NM_MODEM_SIM_OPERATOR_ID, NULL, NULL); } static void supported_ip_families_changed (MMModem *modem, GParamSpec *pspec, gpointer user_data) { NMModemBroadband *self = NM_MODEM_BROADBAND (user_data); g_return_if_fail (modem == self->_priv.modem_iface); g_object_set (G_OBJECT (self), NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem)), NULL); } static void operator_code_changed (MMModem3gpp *modem_3gpp, GParamSpec *pspec, gpointer user_data) { NMModemBroadband *self = NM_MODEM_BROADBAND (user_data); g_return_if_fail (modem_3gpp == self->_priv.modem_3gpp_iface); _nm_modem_set_operator_code (NM_MODEM (self), mm_modem_3gpp_get_operator_code (modem_3gpp)); } /*****************************************************************************/ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMModemBroadband *self = NM_MODEM_BROADBAND (object); switch (prop_id) { case PROP_MODEM: g_value_set_object (value, self->_priv.modem_object); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMModemBroadband *self = NM_MODEM_BROADBAND (object); switch (prop_id) { case PROP_MODEM: /* construct-only */ self->_priv.modem_object = g_value_dup_object (value); self->_priv.modem_iface = mm_object_get_modem (self->_priv.modem_object); g_return_if_fail (self->_priv.modem_iface); self->_priv.modem_3gpp_iface = mm_object_get_modem_3gpp (self->_priv.modem_object); g_signal_connect (self->_priv.modem_iface, "state-changed", G_CALLBACK (modem_state_changed), self); g_signal_connect (self->_priv.modem_iface, "notify::sim", G_CALLBACK (sim_changed), self); sim_changed (self->_priv.modem_iface, NULL, self); g_signal_connect (self->_priv.modem_iface, "notify::supported-ip-families", G_CALLBACK (supported_ip_families_changed), self); if (self->_priv.modem_3gpp_iface) { g_signal_connect (self->_priv.modem_3gpp_iface, "notify::operator-code", G_CALLBACK (operator_code_changed), self); } /* Note: don't grab the Simple iface here; the Modem interface is the * only one assumed to be always valid and available */ break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /*****************************************************************************/ static void nm_modem_broadband_init (NMModemBroadband *self) { } NMModem * nm_modem_broadband_new (GObject *object, GError **error) { MMObject *modem_object; MMModem *modem_iface; MMModem3gpp *modem_3gpp_iface; const char *const*drivers; const char *operator_code = NULL; gs_free char *driver = NULL; g_return_val_if_fail (MM_IS_OBJECT (object), NULL); modem_object = MM_OBJECT (object); /* Ensure we have the 'Modem' interface and the primary port at least */ modem_iface = mm_object_peek_modem (modem_object); g_return_val_if_fail (modem_iface, NULL); g_return_val_if_fail (mm_modem_get_primary_port (modem_iface), NULL); /* Build a single string with all drivers listed */ drivers = mm_modem_get_drivers (modem_iface); if (drivers) driver = g_strjoinv (", ", (char **) drivers); modem_3gpp_iface = mm_object_peek_modem_3gpp (modem_object); if (modem_3gpp_iface) operator_code = mm_modem_3gpp_get_operator_code (modem_3gpp_iface); return g_object_new (NM_TYPE_MODEM_BROADBAND, NM_MODEM_PATH, mm_object_get_path (modem_object), NM_MODEM_UID, mm_modem_get_primary_port (modem_iface), NM_MODEM_CONTROL_PORT, mm_modem_get_primary_port (modem_iface), NM_MODEM_IP_TYPES, mm_ip_family_to_nm (mm_modem_get_supported_ip_families (modem_iface)), NM_MODEM_STATE, (int) mm_state_to_nm (mm_modem_get_state (modem_iface)), NM_MODEM_DEVICE_ID, mm_modem_get_device_identifier (modem_iface), NM_MODEM_BROADBAND_MODEM, modem_object, NM_MODEM_DRIVER, driver, NM_MODEM_OPERATOR_CODE, operator_code, NULL); } static void dispose (GObject *object) { NMModemBroadband *self = NM_MODEM_BROADBAND (object); NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); nm_clear_g_source (&priv->idle_id_ip4); nm_clear_g_source (&priv->idle_id_ip6); connect_context_clear (self); g_clear_object (&self->_priv.ipv4_config); g_clear_object (&self->_priv.ipv6_config); g_clear_object (&self->_priv.bearer); if (self->_priv.modem_iface) { g_signal_handlers_disconnect_by_data (self->_priv.modem_iface, self); g_clear_object (&self->_priv.modem_iface); } if (self->_priv.modem_3gpp_iface) { g_signal_handlers_disconnect_by_data (self->_priv.modem_3gpp_iface, self); g_clear_object (&self->_priv.modem_3gpp_iface); } g_clear_object (&self->_priv.simple_iface); g_clear_object (&self->_priv.sim_iface); g_clear_object (&self->_priv.modem_object); G_OBJECT_CLASS (nm_modem_broadband_parent_class)->dispose (object); } static void nm_modem_broadband_class_init (NMModemBroadbandClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMModemClass *modem_class = NM_MODEM_CLASS (klass); object_class->dispose = dispose; object_class->get_property = get_property; object_class->set_property = set_property; modem_class->get_capabilities = get_capabilities; modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; modem_class->stage3_ip6_config_request = stage3_ip6_config_request; modem_class->disconnect = disconnect; modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->set_mm_enabled = set_mm_enabled; modem_class->get_user_pass = get_user_pass; modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem; modem_class->complete_connection = complete_connection; modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare; modem_class->owns_port = owns_port; obj_properties[PROP_MODEM] = g_param_spec_object (NM_MODEM_BROADBAND_MODEM, "", "", MM_GDBUS_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); }