// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2009 - 2019 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-device-modem.h"
#include "nm-modem.h"
#include "nm-ip4-config.h"
#include "devices/nm-device-private.h"
#include "nm-rfkill-manager.h"
#include "settings/nm-settings-connection.h"
#include "nm-modem-broadband.h"
#include "NetworkManagerUtils.h"
#include "nm-core-internal.h"
#include "devices/nm-device-logging.h"
_LOG_DECLARE_SELF(NMDeviceModem);
/*****************************************************************************/
NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceModem,
PROP_MODEM,
PROP_CAPABILITIES,
PROP_CURRENT_CAPABILITIES,
PROP_DEVICE_ID,
PROP_OPERATOR_CODE,
PROP_APN,
);
typedef struct {
NMModem *modem;
NMDeviceModemCapabilities caps;
NMDeviceModemCapabilities current_caps;
char *device_id;
char *operator_code;
char *apn;
bool rf_enabled:1;
NMDeviceStageState stage1_state:3;
} NMDeviceModemPrivate;
struct _NMDeviceModem {
NMDevice parent;
NMDeviceModemPrivate _priv;
};
struct _NMDeviceModemClass {
NMDeviceClass parent;
};
G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE)
#define NM_DEVICE_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceModem, NM_IS_DEVICE_MODEM, NMDevice)
/*****************************************************************************/
static void
ppp_failed (NMModem *modem,
guint i_reason,
gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
NMDeviceStateReason reason = i_reason;
switch (nm_device_get_state (device)) {
case NM_DEVICE_STATE_PREPARE:
case NM_DEVICE_STATE_CONFIG:
case NM_DEVICE_STATE_NEED_AUTH:
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
break;
case NM_DEVICE_STATE_IP_CONFIG:
case NM_DEVICE_STATE_IP_CHECK:
case NM_DEVICE_STATE_SECONDARIES:
case NM_DEVICE_STATE_ACTIVATED:
if (nm_device_activate_ip4_state_in_conf (device))
nm_device_activate_schedule_ip_config_timeout (device, AF_INET);
else if (nm_device_activate_ip6_state_in_conf (device))
nm_device_activate_schedule_ip_config_timeout (device, AF_INET6);
else if (nm_device_activate_ip4_state_done (device)) {
nm_device_ip_method_failed (device,
AF_INET,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
} else if (nm_device_activate_ip6_state_done (device)) {
nm_device_ip_method_failed (device,
AF_INET6,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
} else {
_LOGW (LOGD_MB, "PPP failure in unexpected state %u", (guint) nm_device_get_state (device));
nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
}
break;
default:
break;
}
}
static void
modem_prepare_result (NMModem *modem,
gboolean success,
guint i_reason,
gpointer user_data)
{
NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
NMDevice *device = NM_DEVICE (self);
NMDeviceStateReason reason = i_reason;
if ( nm_device_get_state (device) != NM_DEVICE_STATE_PREPARE
|| priv->stage1_state != NM_DEVICE_STAGE_STATE_PENDING) {
nm_assert_not_reached ();
success = FALSE;
}
if (!success) {
/* There are several reasons to block autoconnection at device level:
*
* - Wrong SIM-PIN: The device won't autoconnect because it doesn't make sense
* to retry the connection with the same PIN. This error also makes autoconnection
* blocked at settings level, so not even a modem unplug and replug will allow
* autoconnection again. It is somewhat redundant to block autoconnection at
* both device and setting level really.
*
* - SIM wrong or not inserted: If the modem is reporting a SIM not inserted error,
* we can block autoconnection at device level, so that if the same device is
* unplugged and replugged with a SIM (or if a SIM hotplug event happens in MM,
* recreating the device completely), we can try the autoconnection again.
*
* - Modem initialization failed: For some reason unknown to NM, the modem wasn't
* initialized correctly, which leads to an unusable device. A device unplug and
* replug may solve the issue, so make it a device-level autoconnection blocking
* reason.
*/
switch (nm_device_state_reason_check (reason)) {
case NM_DEVICE_STATE_REASON_GSM_SIM_PIN_REQUIRED:
case NM_DEVICE_STATE_REASON_GSM_SIM_PUK_REQUIRED:
case NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT:
nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_WRONG_PIN);
break;
case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
case NM_DEVICE_STATE_REASON_GSM_SIM_WRONG:
nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_SIM_MISSING);
break;
case NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED:
nm_device_autoconnect_blocked_set (device, NM_DEVICE_AUTOCONNECT_BLOCKED_INIT_FAILED);
break;
default:
break;
}
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
return;
}
priv->stage1_state = NM_DEVICE_STAGE_STATE_COMPLETED;
nm_device_activate_schedule_stage1_device_prepare (device, FALSE);
}
static void
modem_auth_requested (NMModem *modem, gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
/* Auth requests (PIN, PAP/CHAP passwords, etc) only get handled
* during activation.
*/
if (!nm_device_is_activating (device))
return;
nm_device_state_changed (device,
NM_DEVICE_STATE_NEED_AUTH,
NM_DEVICE_STATE_REASON_NONE);
}
static void
modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
if (error) {
nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_NO_SECRETS);
return;
}
priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT;
nm_device_activate_schedule_stage1_device_prepare (device, FALSE);
}
static void
modem_ip4_config_result (NMModem *modem,
NMIP4Config *config,
GError *error,
gpointer user_data)
{
NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
NMDevice *device = NM_DEVICE (self);
g_return_if_fail (nm_device_activate_ip4_state_in_conf (device) == TRUE);
if (error) {
_LOGW (LOGD_MB | LOGD_IP4, "retrieving IPv4 configuration failed: %s",
error->message);
nm_device_ip_method_failed (device,
AF_INET,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
} else {
nm_device_set_dev2_ip_config (device, AF_INET, NM_IP_CONFIG_CAST (config));
nm_device_activate_schedule_ip_config_result (device, AF_INET, NULL);
}
}
static void
modem_ip6_config_result (NMModem *modem,
NMIP6Config *config,
gboolean do_slaac,
GError *error,
gpointer user_data)
{
NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
NMDevice *device = NM_DEVICE (self);
NMActStageReturn ret;
NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
gs_unref_object NMIP6Config *ignored = NULL;
gboolean got_config = !!config;
g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE);
if (error) {
_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: %s",
error->message);
nm_device_ip_method_failed (device,
AF_INET6,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
return;
}
/* Re-enable IPv6 on the interface */
nm_device_sysctl_ip_conf_set (device, AF_INET6, "disable_ipv6", "0");
if (config)
nm_device_set_dev2_ip_config (device, AF_INET6, NM_IP_CONFIG_CAST (config));
if (do_slaac == FALSE) {
if (got_config)
nm_device_activate_schedule_ip_config_result (device, AF_INET6, NULL);
else {
_LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: SLAAC not requested and no addresses");
nm_device_ip_method_failed (device,
AF_INET6,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
}
return;
}
/* Start SLAAC now that we have a link-local address from the modem */
ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip_config_start (device, AF_INET6, (gpointer *) &ignored, &failure_reason);
nm_assert (ignored == NULL);
switch (ret) {
case NM_ACT_STAGE_RETURN_FAILURE:
nm_device_ip_method_failed (device, AF_INET6, failure_reason);
break;
case NM_ACT_STAGE_RETURN_IP_FAIL:
/* all done */
nm_device_activate_schedule_ip_config_result (device, AF_INET6, NULL);
break;
case NM_ACT_STAGE_RETURN_POSTPONE:
/* let SLAAC run */
break;
default:
/* Should never get here since we've assured that the IPv6 method
* will either be "auto" or "ignored" when starting IPv6 configuration.
*/
nm_assert_not_reached ();
}
}
static void
ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
if (!nm_device_is_activating (device))
return;
if (!nm_device_set_ip_ifindex (device,
nm_modem_get_ip_ifindex (modem))) {
nm_device_state_changed (device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
return;
}
/* Disable IPv6 immediately on the interface since NM handles IPv6
* internally, and leaving it enabled could allow the kernel's IPv6
* RA handling code to run before NM is ready.
*/
nm_device_sysctl_ip_conf_set (device, AF_INET6, "disable_ipv6", "1");
}
static void
operator_code_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
{
NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
const char *operator_code = nm_modem_get_operator_code (modem);
if (g_strcmp0 (priv->operator_code, operator_code) != 0) {
g_free (priv->operator_code);
priv->operator_code = g_strdup (operator_code);
_notify (self, PROP_OPERATOR_CODE);
}
}
static void
apn_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
{
NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
const char *apn = nm_modem_get_apn (modem);
if (g_strcmp0 (priv->apn, apn) != 0) {
g_free (priv->apn);
priv->apn = g_strdup (apn);
_notify (self, PROP_APN);
}
}
static void
ids_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
{
nm_device_recheck_available_connections (NM_DEVICE (user_data));
}
static void
modem_state_cb (NMModem *modem,
int new_state_i,
int old_state_i,
gpointer user_data)
{
NMModemState new_state = new_state_i;
NMModemState old_state = old_state_i;
NMDevice *device = NM_DEVICE (user_data);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
NMDeviceState dev_state = nm_device_get_state (device);
if (new_state <= NM_MODEM_STATE_DISABLING &&
old_state > NM_MODEM_STATE_DISABLING &&
priv->rf_enabled) {
/* Called when the ModemManager modem enabled state is changed externally
* to NetworkManager (eg something using MM's D-Bus API directly).
*/
if (nm_device_is_activating (device) || dev_state == NM_DEVICE_STATE_ACTIVATED) {
/* user-initiated action, hence DISCONNECTED not FAILED */
nm_device_state_changed (device,
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_USER_REQUESTED);
return;
}
}
if (new_state < NM_MODEM_STATE_CONNECTING &&
old_state >= NM_MODEM_STATE_CONNECTING &&
dev_state >= NM_DEVICE_STATE_NEED_AUTH &&
dev_state <= NM_DEVICE_STATE_ACTIVATED) {
/* Fail the device if the modem disconnects unexpectedly while the
* device is activating/activated. */
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
return;
}
if (new_state > NM_MODEM_STATE_LOCKED && old_state == NM_MODEM_STATE_LOCKED) {
/* If the modem is now unlocked, enable/disable it according to the
* device's enabled/disabled state.
*/
nm_modem_set_mm_enabled (priv->modem, priv->rf_enabled);
if (dev_state == NM_DEVICE_STATE_NEED_AUTH) {
/* The modem was unlocked externally to NetworkManager,
deactivate so the default connection can be
automatically activated again */
nm_device_state_changed (device,
NM_DEVICE_STATE_DEACTIVATING,
NM_DEVICE_STATE_REASON_MODEM_AVAILABLE);
}
/* Now allow connections without a PIN to be available */
nm_device_recheck_available_connections (device);
}
nm_device_queue_recheck_available (device,
NM_DEVICE_STATE_REASON_MODEM_AVAILABLE,
NM_DEVICE_STATE_REASON_MODEM_FAILED);
}
static void
modem_removed_cb (NMModem *modem, gpointer user_data)
{
g_signal_emit_by_name (NM_DEVICE (user_data), NM_DEVICE_REMOVED);
}
/*****************************************************************************/
static gboolean
owns_iface (NMDevice *device, const char *iface)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
g_return_val_if_fail (priv->modem, FALSE);
return nm_modem_owns_port (priv->modem, iface);
}
/*****************************************************************************/
static void
device_state_changed (NMDevice *device,
NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason)
{
NMDeviceModem *self = NM_DEVICE_MODEM (device);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
g_return_if_fail (priv->modem);
if (new_state == NM_DEVICE_STATE_UNAVAILABLE &&
old_state < NM_DEVICE_STATE_UNAVAILABLE) {
/* Log initial modem state */
_LOGI (LOGD_MB, "modem state '%s'",
nm_modem_state_to_string (nm_modem_get_state (priv->modem)));
}
nm_modem_device_state_changed (priv->modem, new_state, old_state);
}
static NMDeviceCapabilities
get_generic_capabilities (NMDevice *device)
{
return NM_DEVICE_CAP_IS_NON_KERNEL;
}
static const char *
get_type_description (NMDevice *device)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS))
return "gsm";
if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_CDMA_EVDO))
return "cdma";
return NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_type_description (device);
}
static gboolean
check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error)
{
GError *local = NULL;
if (!NM_DEVICE_CLASS (nm_device_modem_parent_class)->check_connection_compatible (device, connection, error))
return FALSE;
if (!nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
connection,
error ? &local : NULL)) {
if (error) {
g_set_error (error,
NM_UTILS_ERROR,
g_error_matches (local, NM_UTILS_ERROR, NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE)
? NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE
: NM_UTILS_ERROR_UNKNOWN,
"modem is incompatible with connection: %s",
local->message);
g_error_free (local);
}
return FALSE;
}
return TRUE;
}
static gboolean
check_connection_available (NMDevice *device,
NMConnection *connection,
NMDeviceCheckConAvailableFlags flags,
const char *specific_object,
GError **error)
{
NMDeviceModem *self = NM_DEVICE_MODEM (device);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
NMModemState state;
if (!priv->rf_enabled) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"RFKILL for modem enabled");
return FALSE;
}
if (!priv->modem) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"modem not available");
return FALSE;
}
state = nm_modem_get_state (priv->modem);
if (state <= NM_MODEM_STATE_INITIALIZING) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"modem not initialized");
return FALSE;
}
if (state == NM_MODEM_STATE_LOCKED) {
if (!nm_connection_get_setting_gsm (connection)) {
nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
"modem is locked without pin available");
return FALSE;
}
}
return TRUE;
}
static gboolean
complete_connection (NMDevice *device,
NMConnection *connection,
const char *specific_object,
NMConnection *const*existing_connections,
GError **error)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
return nm_modem_complete_connection (priv->modem,
nm_device_get_iface (device),
connection,
existing_connections,
error);
}
static void
deactivate (NMDevice *device)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
nm_modem_deactivate (priv->modem, device);
priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT;
}
/*****************************************************************************/
static void
modem_deactivate_async_cb (NMModem *modem,
GError *error,
gpointer user_data)
{
gs_unref_object NMDevice *self = NULL;
NMDeviceDeactivateCallback callback;
gpointer callback_user_data;
nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data);
callback (self, error, callback_user_data);
}
static void
deactivate_async (NMDevice *self,
GCancellable *cancellable,
NMDeviceDeactivateCallback callback,
gpointer user_data)
{
nm_assert (G_IS_CANCELLABLE (cancellable));
nm_assert (callback);
nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE (self)->modem,
self,
cancellable,
modem_deactivate_async_cb,
nm_utils_user_data_pack (g_object_ref (self),
callback,
user_data));
}
/*****************************************************************************/
static NMActStageReturn
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
NMActRequest *req;
req = nm_device_get_act_request (device);
g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
if (priv->stage1_state == NM_DEVICE_STAGE_STATE_INIT) {
priv->stage1_state = NM_DEVICE_STAGE_STATE_PENDING;
return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem,
req,
out_failure_reason);
}
if (priv->stage1_state == NM_DEVICE_STAGE_STATE_PENDING)
return NM_ACT_STAGE_RETURN_POSTPONE;
nm_assert (priv->stage1_state == NM_DEVICE_STAGE_STATE_COMPLETED);
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
{
nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem);
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
act_stage3_ip_config_start (NMDevice *device,
int addr_family,
gpointer *out_config,
NMDeviceStateReason *out_failure_reason)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
nm_assert_addr_family (addr_family);
if (addr_family == AF_INET) {
return nm_modem_stage3_ip4_config_start (priv->modem,
device,
NM_DEVICE_CLASS (nm_device_modem_parent_class),
out_failure_reason);
} else {
return nm_modem_stage3_ip6_config_start (priv->modem,
device,
out_failure_reason);
}
}
static void
ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
{
nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device, config);
}
static gboolean
get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid)
{
NMDeviceModem *self = NM_DEVICE_MODEM (device);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
gboolean success;
g_return_val_if_fail (priv->modem, FALSE);
success = nm_modem_get_iid (priv->modem, out_iid);
if (!success)
success = NM_DEVICE_CLASS (nm_device_modem_parent_class)->get_ip_iface_identifier (device, out_iid);
return success;
}
/*****************************************************************************/
static gboolean
get_enabled (NMDevice *device)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device);
NMModemState modem_state = nm_modem_get_state (priv->modem);
return priv->rf_enabled && (modem_state >= NM_MODEM_STATE_LOCKED);
}
static void
set_enabled (NMDevice *device, gboolean enabled)
{
NMDeviceModem *self = NM_DEVICE_MODEM (device);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
/* Called only by the Manager in response to rfkill switch changes or
* global user WWAN enable/disable preference changes.
*/
priv->rf_enabled = enabled;
if (priv->modem) {
/* Sync the ModemManager modem enabled/disabled with rfkill/user preference */
nm_modem_set_mm_enabled (priv->modem, enabled);
}
if (enabled == FALSE) {
nm_device_state_changed (device,
NM_DEVICE_STATE_UNAVAILABLE,
NM_DEVICE_STATE_REASON_NONE);
}
}
static gboolean
is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
{
NMDeviceModem *self = NM_DEVICE_MODEM (device);
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
NMModemState modem_state;
if (!priv->rf_enabled)
return FALSE;
g_assert (priv->modem);
modem_state = nm_modem_get_state (priv->modem);
if (modem_state <= NM_MODEM_STATE_INITIALIZING)
return FALSE;
return TRUE;
}
/*****************************************************************************/
static void
set_modem (NMDeviceModem *self, NMModem *modem)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
g_return_if_fail (modem != NULL);
priv->modem = nm_modem_claim (modem);
g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self);
g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self);
g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self);
g_signal_connect (modem, NM_MODEM_IP6_CONFIG_RESULT, G_CALLBACK (modem_ip6_config_result), self);
g_signal_connect (modem, NM_MODEM_AUTH_REQUESTED, G_CALLBACK (modem_auth_requested), self);
g_signal_connect (modem, NM_MODEM_AUTH_RESULT, G_CALLBACK (modem_auth_result), self);
g_signal_connect (modem, NM_MODEM_STATE_CHANGED, G_CALLBACK (modem_state_cb), self);
g_signal_connect (modem, NM_MODEM_REMOVED, G_CALLBACK (modem_removed_cb), self);
g_signal_connect (modem, "notify::" NM_MODEM_IP_IFINDEX, G_CALLBACK (ip_ifindex_changed_cb), self);
g_signal_connect (modem, "notify::" NM_MODEM_DEVICE_ID, G_CALLBACK (ids_changed_cb), self);
g_signal_connect (modem, "notify::" NM_MODEM_SIM_ID, G_CALLBACK (ids_changed_cb), self);
g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self);
g_signal_connect (modem, "notify::" NM_MODEM_OPERATOR_CODE, G_CALLBACK (operator_code_changed_cb), self);
g_signal_connect (modem, "notify::" NM_MODEM_APN, G_CALLBACK (apn_changed_cb), self);
}
static guint32
get_dhcp_timeout_for_device (NMDevice *device, int addr_family)
{
/* DHCP is always done by the modem firmware, not by the network, and
* by the time we get around to DHCP the firmware should already know
* the IP addressing details. So the DHCP timeout can be much shorter.
*/
return 15;
}
/*****************************************************************************/
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
switch (prop_id) {
case PROP_MODEM:
g_value_set_object (value, priv->modem);
break;
case PROP_CAPABILITIES:
g_value_set_uint (value, priv->caps);
break;
case PROP_CURRENT_CAPABILITIES:
g_value_set_uint (value, priv->current_caps);
break;
case PROP_DEVICE_ID:
g_value_set_string (value, priv->device_id);
break;
case PROP_OPERATOR_CODE:
g_value_set_string (value, priv->operator_code);
break;
case PROP_APN:
g_value_set_string (value, priv->apn);
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)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
switch (prop_id) {
case PROP_MODEM:
/* construct-only */
set_modem (NM_DEVICE_MODEM (object), g_value_get_object (value));
break;
case PROP_CAPABILITIES:
priv->caps = g_value_get_uint (value);
break;
case PROP_CURRENT_CAPABILITIES:
priv->current_caps = g_value_get_uint (value);
break;
case PROP_DEVICE_ID:
/* construct-only */
priv->device_id = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/*****************************************************************************/
static void
nm_device_modem_init (NMDeviceModem *self)
{
}
NMDevice *
nm_device_modem_new (NMModem *modem)
{
NMDeviceModemCapabilities caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
NMDeviceModemCapabilities current_caps = NM_DEVICE_MODEM_CAPABILITY_NONE;
g_return_val_if_fail (NM_IS_MODEM (modem), NULL);
/* Load capabilities */
nm_modem_get_capabilities (modem, &caps, ¤t_caps);
return g_object_new (NM_TYPE_DEVICE_MODEM,
NM_DEVICE_UDI, nm_modem_get_path (modem),
NM_DEVICE_IFACE, nm_modem_get_uid (modem),
NM_DEVICE_DRIVER, nm_modem_get_driver (modem),
NM_DEVICE_TYPE_DESC, "Broadband",
NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_MODEM,
NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WWAN,
NM_DEVICE_MODEM_MODEM, modem,
NM_DEVICE_MODEM_CAPABILITIES, caps,
NM_DEVICE_MODEM_CURRENT_CAPABILITIES, current_caps,
NM_DEVICE_MODEM_DEVICE_ID, nm_modem_get_device_id (modem),
NULL);
}
static void
dispose (GObject *object)
{
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object);
if (priv->modem) {
g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object));
nm_clear_pointer (&priv->modem, nm_modem_unclaim);
}
nm_clear_g_free (&priv->device_id);
nm_clear_g_free (&priv->operator_code);
nm_clear_g_free (&priv->apn);
G_OBJECT_CLASS (nm_device_modem_parent_class)->dispose (object);
}
static const NMDBusInterfaceInfoExtended interface_info_device_modem = {
.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
NM_DBUS_INTERFACE_DEVICE_MODEM,
.signals = NM_DEFINE_GDBUS_SIGNAL_INFOS (
&nm_signal_info_property_changed_legacy,
),
.properties = NM_DEFINE_GDBUS_PROPERTY_INFOS (
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("ModemCapabilities", "u", NM_DEVICE_MODEM_CAPABILITIES),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("CurrentCapabilities", "u", NM_DEVICE_MODEM_CURRENT_CAPABILITIES),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("DeviceId", "s", NM_DEVICE_MODEM_DEVICE_ID),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("OperatorCode", "s", NM_DEVICE_MODEM_OPERATOR_CODE),
NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Apn", "s", NM_DEVICE_MODEM_APN),
),
),
.legacy_property_changed = TRUE,
};
static void
nm_device_modem_class_init (NMDeviceModemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
object_class->dispose = dispose;
object_class->get_property = get_property;
object_class->set_property = set_property;
dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_modem);
device_class->get_generic_capabilities = get_generic_capabilities;
device_class->get_type_description = get_type_description;
device_class->check_connection_compatible = check_connection_compatible;
device_class->check_connection_available = check_connection_available;
device_class->complete_connection = complete_connection;
device_class->deactivate_async = deactivate_async;
device_class->deactivate = deactivate;
device_class->act_stage1_prepare = act_stage1_prepare;
device_class->act_stage2_config = act_stage2_config;
device_class->act_stage3_ip_config_start = act_stage3_ip_config_start;
device_class->ip4_config_pre_commit = ip4_config_pre_commit;
device_class->get_enabled = get_enabled;
device_class->set_enabled = set_enabled;
device_class->owns_iface = owns_iface;
device_class->is_available = is_available;
device_class->get_ip_iface_identifier = get_ip_iface_identifier;
device_class->get_configured_mtu = nm_modem_get_configured_mtu;
device_class->get_dhcp_timeout_for_device = get_dhcp_timeout_for_device;
device_class->state_changed = device_state_changed;
obj_properties[PROP_MODEM] =
g_param_spec_object (NM_DEVICE_MODEM_MODEM, "", "",
NM_TYPE_MODEM,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_CAPABILITIES] =
g_param_spec_uint (NM_DEVICE_MODEM_CAPABILITIES, "", "",
0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_CURRENT_CAPABILITIES] =
g_param_spec_uint (NM_DEVICE_MODEM_CURRENT_CAPABILITIES, "", "",
0, G_MAXUINT32, NM_DEVICE_MODEM_CAPABILITY_NONE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_DEVICE_ID] =
g_param_spec_string (NM_DEVICE_MODEM_DEVICE_ID, "", "",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_OPERATOR_CODE] =
g_param_spec_string (NM_DEVICE_MODEM_OPERATOR_CODE, "", "",
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
obj_properties[PROP_APN] =
g_param_spec_string (NM_DEVICE_MODEM_APN, "", "",
NULL,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
}