This new object, which is a subclass of `NMModem', implements the basic support
of the new ModemManager1 interface.
---
 src/generated/Makefile.am              |   18 +-
 src/modem-manager/Makefile.am          |    8 +
 src/modem-manager/README               |    9 +
 src/modem-manager/nm-modem-broadband.c | 1007 ++++++++++++++++++++++++++++++++
 src/modem-manager/nm-modem-broadband.h |   71 +++
 5 files changed, 1112 insertions(+), 1 deletion(-)
 create mode 100644 src/modem-manager/nm-modem-broadband.c
 create mode 100644 src/modem-manager/nm-modem-broadband.h

diff --git a/src/generated/Makefile.am b/src/generated/Makefile.am
index 7332644..9e38580 100644
--- a/src/generated/Makefile.am
+++ b/src/generated/Makefile.am
@@ -27,9 +27,21 @@ if WITH_WIMAX
 nm_daemon_all_sources += $(top_srcdir)/src/wimax/*.[ch]
 endif
 
-nm_daemon_sources = \
+nm_daemon_sources_no_bindings = \
        $(filter-out %-glue.h %-bindings.h, $(wildcard 
$(nm_daemon_all_sources)))
 
+if WITH_MODEM_MANAGER_1
+# We filter out this file, which doesn't have any enum, and which clashes
+# with ModemManager1-defined symbols
+nm_daemon_sources = \
+       $(filter-out $(top_srcdir)/src/modem-manager/nm-modem-types.h, 
$(wildcard $(nm_daemon_sources_no_bindings)))
+else
+# Don't include ModemManager1-specific headers if we're not compiling with
+# ModemManager1 support.
+nm_daemon_sources = \
+       $(filter-out $(top_srcdir)/src/modem-manager/nm-modem-broadband.h, 
$(wildcard $(nm_daemon_sources_no_bindings)))
+endif
+
 GLIB_GENERATED = nm-enum-types.h nm-enum-types.c
 nm_enum_types_sources = $(nm_daemon_sources)
 GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM
@@ -71,6 +83,10 @@ libnm_generated_la_CPPFLAGS = \
        $(SYSTEMD_CFLAGS) \
        $(IWMX_SDK_CFLAGS)
 
+if WITH_MODEM_MANAGER_1
+       libnm_generated_la_CPPFLAGS += $(MM_GLIB_CFLAGS)
+endif
+
 libnm_generated_la_LIBADD = \
        $(GLIB_LIBS)
 
diff --git a/src/modem-manager/Makefile.am b/src/modem-manager/Makefile.am
index 192ed2e..aa5534f 100644
--- a/src/modem-manager/Makefile.am
+++ b/src/modem-manager/Makefile.am
@@ -35,3 +35,11 @@ libmodem_manager_la_LIBADD = \
        $(LIBNL_LIBS) \
        $(DBUS_LIBS)
 
+# Support for the new ModemManager1 interface
+if WITH_MODEM_MANAGER_1
+libmodem_manager_la_SOURCES += \
+       nm-modem-broadband.h \
+       nm-modem-broadband.c
+libmodem_manager_la_CPPFLAGS += $(MM_GLIB_CFLAGS)
+libmodem_manager_la_LIBADD += $(MM_GLIB_LIBS)
+endif
diff --git a/src/modem-manager/README b/src/modem-manager/README
index eba36ba..d3f9122 100644
--- a/src/modem-manager/README
+++ b/src/modem-manager/README
@@ -11,6 +11,15 @@ Common source
       the modem object.
 
 
+ModemManager 0.7 integration
+********************************************************************************
+
+ * nm-modem-broadband.[h|c]:
+      Defines the `NMModemBroadband' object, which is a subclass of `NMModem'.
+      This object handles both 3GPP and 3GPP2 modems exposed in the new
+      `ModemManager1' interface.
+
+
 ModemManager 0.4/0.5/0.6 integration
 
********************************************************************************
 
diff --git a/src/modem-manager/nm-modem-broadband.c 
b/src/modem-manager/nm-modem-broadband.c
new file mode 100644
index 0000000..45e37fa
--- /dev/null
+++ b/src/modem-manager/nm-modem-broadband.c
@@ -0,0 +1,1007 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Aleksander Morgado <[email protected]>
+ */
+
+#include <glib/gi18n.h>
+#include <string.h>
+#include "nm-modem-broadband.h"
+#include "nm-system.h"
+#include "nm-setting-connection.h"
+#include "nm-logging.h"
+#include "NetworkManagerUtils.h"
+#include "nm-device-private.h"
+
+G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
+
+struct _NMModemBroadbandPrivate {
+       /* The modem object from dbus */
+       MMObject *modem_object;
+       /* Per-interface objects */
+       MMModem *modem_iface;
+       MMModemSimple *simple_iface;
+
+       /* Connection setup */
+       MMSimpleConnectProperties *connect_properties;
+       MMBearer *bearer;
+       MMBearerIpConfig *ipv4_config;
+       MMBearerIpConfig *ipv6_config;
+
+       guint32 pin_tries;
+};
+
+enum {
+       PROP_0,
+       PROP_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))
+
+/*****************************************************************************/
+
+#define NM_MODEM_BROADBAND_ERROR (nm_modem_broadband_error_quark ())
+
+static GQuark
+nm_modem_broadband_error_quark (void)
+{
+       static GQuark quark = 0;
+       if (!quark)
+               quark = g_quark_from_static_string ("nm-modem-broadband-error");
+       return quark;
+}
+
+static NMDeviceStateReason
+translate_mm_error (GError *error)
+{
+       NMDeviceStateReason reason;
+
+       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 {
+               /* unable to map the ModemManager error to a 
NM_DEVICE_STATE_REASON */
+               nm_log_dbg (LOGD_MB, "unmapped error detected: '%s'", 
error->message);
+               reason = NM_DEVICE_STATE_REASON_UNKNOWN;
+       }
+
+       return reason;
+}
+
+/*****************************************************************************/
+
+void
+nm_modem_broadband_get_capabilities (NMModemBroadband *self,
+                                     NMDeviceModemCapabilities *modem_caps,
+                                     NMDeviceModemCapabilities *current_caps)
+{
+       *modem_caps = 
(NMDeviceModemCapabilities)mm_modem_get_modem_capabilities 
(self->priv->modem_iface);
+       *current_caps = 
(NMDeviceModemCapabilities)mm_modem_get_current_capabilities 
(self->priv->modem_iface);
+}
+
+/*****************************************************************************/
+
+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 void
+connect_ready (MMModemSimple *simple_iface,
+               GAsyncResult *res,
+               NMModemBroadband *self)
+{
+       GError *error = NULL;
+
+       g_clear_object (&self->priv->connect_properties);
+
+       self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, 
&error);
+       if (!self->priv->bearer) {
+               if (g_error_matches (error,
+                                    MM_MOBILE_EQUIPMENT_ERROR,
+                                    MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN))
+                       ask_for_pin (self);
+               else {
+                       nm_log_warn (LOGD_MB, "(%s) failed to connect modem: 
%s",
+                                    nm_modem_get_uid (NM_MODEM (self)),
+                                    error && error->message ? error->message : 
"(unknown)");
+                       g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, 
FALSE, translate_mm_error (error));
+               }
+               g_clear_error (&error);
+       } else {
+               guint ip_method;
+
+               /* Grab IP configurations */
+               self->priv->ipv4_config = mm_bearer_get_ipv4_config 
(self->priv->bearer);
+               self->priv->ipv6_config = mm_bearer_get_ipv6_config 
(self->priv->bearer);
+
+               switch (mm_bearer_ip_config_get_method 
(self->priv->ipv4_config)) {
+               case MM_BEARER_IP_METHOD_UNKNOWN:
+                       error = g_error_new (NM_MODEM_BROADBAND_ERROR,
+                                            
NM_MODEM_BROADBAND_ERROR_CONNECTION_INVALID,
+                                            "invalid IP config");
+                       nm_log_warn (LOGD_MB, "(%s) failed to connect modem: 
%s",
+                                    nm_modem_get_uid (NM_MODEM (self)),
+                                    error->message);
+                       g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, 
FALSE, translate_mm_error (error));
+                       g_error_free (error);
+                       return;
+               case MM_BEARER_IP_METHOD_PPP:
+                       ip_method = MM_MODEM_IP_METHOD_PPP;
+                       break;
+               case MM_BEARER_IP_METHOD_STATIC:
+                       ip_method = MM_MODEM_IP_METHOD_STATIC;
+                       break;
+               case MM_BEARER_IP_METHOD_DHCP:
+                       ip_method = MM_MODEM_IP_METHOD_DHCP;
+                       break;
+               }
+
+               /* IPv4 for now only */
+               g_object_set (self,
+                             NM_MODEM_DATA_PORT,  mm_bearer_get_interface 
(self->priv->bearer),
+                             NM_MODEM_IP_METHOD,  ip_method,
+                             NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout 
(self->priv->bearer),
+                             NULL);
+
+               g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, 
NM_DEVICE_STATE_REASON_NONE);
+       }
+
+       /* Balance refcount */
+       g_object_unref (self);
+}
+
+
+static MMSimpleConnectProperties *
+create_cdma_connect_properties (NMConnection *connection)
+{
+       NMSettingCdma *setting;
+       MMSimpleConnectProperties *properties;
+       const gchar *str;
+
+       setting = nm_connection_get_setting_cdma (connection);
+       properties = mm_simple_connect_properties_new ();
+
+       str = nm_setting_cdma_get_number (setting);
+       if (str)
+               mm_simple_connect_properties_set_number (properties, str);
+
+       return properties;
+}
+
+static MMSimpleConnectProperties *
+create_gsm_connect_properties (NMConnection *connection)
+{
+       NMSettingGsm *setting;
+       NMSettingPPP *s_ppp;
+       MMSimpleConnectProperties *properties;
+       const gchar *str;
+
+       setting = nm_connection_get_setting_gsm (connection);
+       properties = mm_simple_connect_properties_new ();
+
+       /* TODO: not needed */
+       str = nm_setting_gsm_get_number (setting);
+       if (str)
+               mm_simple_connect_properties_set_number (properties, str);
+
+       str = nm_setting_gsm_get_apn (setting);
+       if (str)
+               mm_simple_connect_properties_set_apn (properties, str);
+
+       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);
+
+       str = nm_setting_gsm_get_username (setting);
+       if (str)
+               mm_simple_connect_properties_set_user (properties, str);
+
+       str = nm_setting_gsm_get_password (setting);
+       if (str)
+               mm_simple_connect_properties_set_password (properties, str);
+
+       /* TODO: We should check SUPPORTED MODES here */
+       switch (nm_setting_gsm_get_network_type (setting)) {
+       case NM_SETTING_GSM_NETWORK_TYPE_UMTS_HSPA:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_3G,
+                                                               
MM_MODEM_MODE_NONE);
+               break;
+       case NM_SETTING_GSM_NETWORK_TYPE_GPRS_EDGE:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_2G,
+                                                               
MM_MODEM_MODE_NONE);
+               break;
+       case NM_SETTING_GSM_NETWORK_TYPE_PREFER_UMTS_HSPA:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_ANY,
+                                                               
MM_MODEM_MODE_3G);
+               break;
+       case NM_SETTING_GSM_NETWORK_TYPE_PREFER_GPRS_EDGE:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_ANY,
+                                                               
MM_MODEM_MODE_2G);
+               break;
+       case NM_SETTING_GSM_NETWORK_TYPE_PREFER_4G:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_ANY,
+                                                               
MM_MODEM_MODE_4G);
+               break;
+       case NM_SETTING_GSM_NETWORK_TYPE_4G:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_4G,
+                                                               
MM_MODEM_MODE_NONE);
+               break;
+       default:
+               mm_simple_connect_properties_set_allowed_modes (properties,
+                                                               
MM_MODEM_MODE_ANY,
+                                                               
MM_MODEM_MODE_NONE);
+               break;
+       }
+
+       /* 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 NMActStageReturn
+act_stage1_prepare (NMModem *_self,
+                    NMActRequest *req,
+                    GPtrArray **out_hints,
+                    const char **out_setting_name,
+                    NMDeviceStateReason *reason)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+       NMConnection *connection;
+
+       connection = nm_act_request_get_connection (req);
+       g_assert (connection);
+
+       *out_setting_name = nm_connection_need_secrets (connection, out_hints);
+       if (!*out_setting_name) {
+               MMModemCapability caps;
+
+               caps = mm_modem_get_current_capabilities 
(self->priv->modem_iface);
+
+               g_clear_object (&self->priv->connect_properties);
+
+               if (MODEM_CAPS_3GPP (caps))
+                       self->priv->connect_properties = 
create_gsm_connect_properties (connection);
+               else if (MODEM_CAPS_3GPP2 (caps))
+                       self->priv->connect_properties = 
create_cdma_connect_properties (connection);
+               else
+                       g_assert_not_reached ();
+
+               if (!self->priv->simple_iface)
+                       self->priv->simple_iface = mm_object_get_modem_simple 
(self->priv->modem_object);
+
+               mm_modem_simple_connect (self->priv->simple_iface,
+                                        self->priv->connect_properties,
+                                        NULL,
+                                        (GAsyncReadyCallback)connect_ready,
+                                        g_object_ref (self));
+       } else {
+               /* NMModem will handle requesting secrets... */
+       }
+
+       return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+/*****************************************************************************/
+
+static NMConnection *
+get_best_auto_connection (NMModem *_self,
+                          GSList *connections,
+                          char **specific_object)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+       MMModemCapability modem_caps;
+       GSList *iter;
+
+       modem_caps = mm_modem_get_current_capabilities 
(self->priv->modem_iface);
+
+       for (iter = connections; iter; iter = g_slist_next (iter)) {
+               NMConnection *connection = NM_CONNECTION (iter->data);
+               NMSettingConnection *s_con;
+
+               s_con = nm_connection_get_setting_connection (connection);
+               g_assert (s_con);
+
+               if (!nm_setting_connection_get_autoconnect (s_con))
+                       continue;
+
+               /* If GSM settings given and our modem is 3GPP, those are the 
best ones */
+               if (   g_str_equal (nm_setting_connection_get_connection_type 
(s_con),
+                                   NM_SETTING_GSM_SETTING_NAME)
+                   && MODEM_CAPS_3GPP (modem_caps))
+                       return connection;
+
+               /* If CDMA settings given and our modem is 3GPP2, return those 
*/
+               if (   g_str_equal (nm_setting_connection_get_connection_type 
(s_con),
+                                   NM_SETTING_CDMA_SETTING_NAME)
+                   && MODEM_CAPS_3GPP2 (modem_caps))
+                       return connection;
+
+               /* continue */
+       }
+
+       return NULL;
+}
+
+/*****************************************************************************/
+
+static gboolean
+check_connection_compatible (NMModem *_self,
+                             NMConnection *connection,
+                             GError **error)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+       MMModemCapability modem_caps;
+       NMSettingConnection *s_con;
+
+       modem_caps = mm_modem_get_current_capabilities 
(self->priv->modem_iface);
+       s_con = nm_connection_get_setting_connection (connection);
+       g_assert (s_con);
+
+       if (MODEM_CAPS_3GPP (modem_caps)) {
+               NMSettingGsm *s_gsm;
+
+               if (!g_str_equal (nm_setting_connection_get_connection_type 
(s_con),
+                                 NM_SETTING_GSM_SETTING_NAME)) {
+                       g_set_error (error,
+                                    NM_MODEM_BROADBAND_ERROR,
+                                    
NM_MODEM_BROADBAND_ERROR_CONNECTION_NOT_GSM,
+                                    "The connection was not a 3GPP 
connection.");
+                       return FALSE;
+               }
+
+               s_gsm = nm_connection_get_setting_gsm (connection);
+               if (!s_gsm) {
+                       g_set_error (error,
+                                    NM_MODEM_BROADBAND_ERROR,
+                                    
NM_MODEM_BROADBAND_ERROR_CONNECTION_INVALID,
+                                    "The connection was not a valid 3GPP 
connection.");
+                       return FALSE;
+               }
+
+               return TRUE;
+       }
+
+       if (MODEM_CAPS_3GPP2 (modem_caps)) {
+               NMSettingCdma *s_cdma;
+
+               if (!g_str_equal (nm_setting_connection_get_connection_type 
(s_con),
+                                 NM_SETTING_CDMA_SETTING_NAME)) {
+                       g_set_error (error,
+                                    NM_MODEM_BROADBAND_ERROR,
+                                    
NM_MODEM_BROADBAND_ERROR_CONNECTION_NOT_CDMA,
+                                    "The connection was not a 3GPP2 
connection.");
+                       return FALSE;
+               }
+
+               s_cdma = nm_connection_get_setting_cdma (connection);
+               if (!s_cdma) {
+                       g_set_error (error,
+                                    NM_MODEM_BROADBAND_ERROR,
+                                    
NM_MODEM_BROADBAND_ERROR_CONNECTION_INVALID,
+                                    "The connection was not a valid 3GPP2 
connection.");
+                       return FALSE;
+               }
+
+               return TRUE;
+       }
+
+       g_assert_not_reached ();
+}
+
+/*****************************************************************************/
+
+static gboolean
+complete_connection (NMModem *_self,
+                     NMConnection *connection,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+       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 || !nm_setting_gsm_get_apn (s_gsm)) {
+                       /* Need an APN at least */
+                       g_set_error_literal (error,
+                                            NM_SETTING_GSM_ERROR,
+                                            
NM_SETTING_GSM_ERROR_MISSING_PROPERTY,
+                                            NM_SETTING_GSM_APN);
+                       return FALSE;
+               }
+
+               /* TODO: This is not needed */
+               if (!nm_setting_gsm_get_number (s_gsm))
+                       g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, 
"*99#", NULL);
+
+               nm_utils_complete_generic (connection,
+                                          NM_SETTING_GSM_SETTING_NAME,
+                                          existing_connections,
+                                          _("GSM connection %d"),
+                                          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 (connection,
+                                          NM_SETTING_CDMA_SETTING_NAME,
+                                          existing_connections,
+                                          _("CDMA connection %d"),
+                                          NULL,
+                                          FALSE); /* No IPv6 yet by default */
+
+               return TRUE;
+       }
+
+       g_assert_not_reached ();
+}
+
+/*****************************************************************************/
+
+static gboolean
+get_user_pass (NMModem *modem,
+               NMConnection *connection,
+               const char **user,
+               const char **pass)
+{
+       NMSettingGsm *s_gsm;
+
+       s_gsm = nm_connection_get_setting_gsm (connection);
+       if (!s_gsm)
+               return FALSE;
+
+       if (user)
+               *user = nm_setting_gsm_get_username (s_gsm);
+       if (pass)
+               *pass = nm_setting_gsm_get_password (s_gsm);
+
+       return TRUE;
+}
+
+/*****************************************************************************/
+
+static const char *
+get_setting_name (NMModem *_self)
+{
+       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))
+               return NM_SETTING_GSM_SETTING_NAME;
+
+       if (MODEM_CAPS_3GPP2 (modem_caps))
+               return NM_SETTING_CDMA_SETTING_NAME;
+
+       return "unknown";
+}
+
+/*****************************************************************************/
+/* Query/Update enabled state */
+
+static void
+update_mm_enabled (NMModem *self,
+                   gboolean new_enabled)
+{
+       if (nm_modem_get_mm_enabled (self) != new_enabled) {
+               g_object_set (self,
+                             NM_MODEM_ENABLED, new_enabled,
+                             NULL);
+       }
+}
+
+static void
+modem_disable_ready (MMModem *modem_iface,
+                     GAsyncResult *res,
+                     NMModemBroadband *self)
+{
+       GError *error = NULL;
+
+       if (!mm_modem_disable_finish (modem_iface, res, &error)) {
+               nm_log_warn (LOGD_MB, "(%s) failed to disable modem: %s",
+                            nm_modem_get_uid (NM_MODEM (self)),
+                            error && error->message ? error->message : 
"(unknown)");
+               g_clear_error (&error);
+       } else
+               /* Update enabled/disabled state again */
+               update_mm_enabled (NM_MODEM (self), FALSE);
+
+       /* 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)) {
+               nm_log_warn (LOGD_MB, "(%s) failed to enable modem: %s",
+                            nm_modem_get_uid (NM_MODEM (self)),
+                            error && error->message ? error->message : 
"(unknown)");
+               g_clear_error (&error);
+       } else
+               update_mm_enabled (NM_MODEM (self), TRUE);
+
+       /* 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));
+
+               /* When disabling don't say we're enabled */
+               update_mm_enabled (NM_MODEM (self), enabled);
+       }
+}
+
+/*****************************************************************************/
+/* IP method static */
+
+static gboolean
+ip_string_to_network_address (const gchar *str,
+                              guint32 *out)
+{
+       struct in_addr addr;
+
+       /* IP address */
+       if (inet_pton (AF_INET, str, &addr) <= 0)
+               return FALSE;
+
+       *out = (guint32)addr.s_addr;
+       return TRUE;
+}
+
+static gboolean
+static_stage3_done (NMModemBroadband *self)
+{
+       GError *error = NULL;
+       NMIP4Config *config = NULL;
+       const gchar *address_string;
+       guint32 address_network;
+       NMIP4Address *addr;
+       const gchar **dns;
+       guint i;
+       guint prefix;
+
+       g_assert (self->priv->ipv4_config);
+
+       nm_log_info (LOGD_MB, "(%s): IPv4 static configuration:",
+                    nm_modem_get_uid (NM_MODEM (self)));
+
+       /* Fully fail if invalid IP address retrieved */
+       address_string = mm_bearer_ip_config_get_address 
(self->priv->ipv4_config);
+       if (!ip_string_to_network_address (address_string, &address_network)) {
+               error = g_error_new (NM_MODEM_BROADBAND_ERROR,
+                                    
NM_MODEM_BROADBAND_ERROR_CONNECTION_INVALID,
+                                    "(%s) retrieving IP4 configuration failed: 
invalid address given '%s'",
+                                    nm_modem_get_uid (NM_MODEM (self)),
+                                    address_string);
+               goto out;
+       }
+
+       config = nm_ip4_config_new ();
+       addr = nm_ip4_address_new ();
+       nm_ip4_address_set_address (addr, address_network);
+       prefix = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config);
+       if (prefix > 0)
+               nm_ip4_address_set_prefix (addr, prefix);
+       nm_ip4_config_take_address (config, addr);
+
+       nm_log_info (LOGD_MB, "  address %s/%d",
+                    mm_bearer_ip_config_get_address (self->priv->ipv4_config),
+                    mm_bearer_ip_config_get_prefix (self->priv->ipv4_config));
+
+       /* DNS servers */
+       dns = mm_bearer_ip_config_get_dns (self->priv->ipv4_config);
+       for (i = 0; dns[i]; i++) {
+               if (   ip_string_to_network_address (dns[i], &address_network)
+                   && address_network > 0) {
+                       nm_ip4_config_add_nameserver (config, address_network);
+                       nm_log_info (LOGD_MB, "  DNS %s", dns[i]);
+               }
+       }
+
+out:
+       g_signal_emit_by_name (self,
+                              NM_MODEM_IP4_CONFIG_RESULT,
+                              mm_bearer_get_interface (self->priv->bearer),
+                              config,
+                              error);
+       g_clear_error (&error);
+       return FALSE;
+}
+
+static NMActStageReturn
+static_stage3_ip4_config_start (NMModem *_self,
+                                NMActRequest *req,
+                                NMDeviceStateReason *reason)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+
+       /* We schedule it in an idle just to follow the same logic as in the
+        * generic modem implementation. */
+       g_idle_add ((GSourceFunc)static_stage3_done, self);
+
+       return NM_ACT_STAGE_RETURN_POSTPONE;
+}
+
+/*****************************************************************************/
+/* Disconnect */
+
+typedef struct {
+       NMModemBroadband *self;
+       gboolean warn;
+} SimpleDisconnectContext;
+
+static void
+simple_disconnect_context_free (SimpleDisconnectContext *ctx)
+{
+       g_object_unref (ctx->self);
+       g_slice_free (SimpleDisconnectContext, ctx);
+}
+
+static void
+simple_disconnect_ready (MMModemSimple *modem_iface,
+                         GAsyncResult *res,
+                         SimpleDisconnectContext *ctx)
+{
+       GError *error = NULL;
+
+       if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) {
+               if (ctx->warn)
+                       nm_log_warn (LOGD_MB, "(%s) failed to disconnect modem: 
%s",
+                                    nm_modem_get_uid (NM_MODEM (ctx->self)),
+                                    error && error->message ? error->message : 
"(unknown)");
+               g_clear_error (&error);
+       }
+
+       simple_disconnect_context_free (ctx);
+}
+
+static void
+disconnect (NMModem *self,
+            gboolean warn)
+{
+       SimpleDisconnectContext *ctx;
+
+       ctx = g_slice_new (SimpleDisconnectContext);
+       ctx->self = g_object_ref (self);
+
+       /* Don't bother warning on FAILED since the modem is already gone */
+       ctx->warn = warn;
+
+       mm_modem_simple_disconnect (
+               ctx->self->priv->simple_iface,
+               NULL, /* bearer path; if NULL given ALL get disconnected */
+               NULL, /* cancellable */
+               (GAsyncReadyCallback)simple_disconnect_ready,
+               ctx);
+}
+
+/*****************************************************************************/
+
+static void
+deactivate (NMModem *_self, NMDevice *device)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
+
+       /* 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;
+
+       /* Chain up parent's */
+       NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate (_self, 
device);
+}
+
+/*****************************************************************************/
+
+static void
+modem_state_changed (MMModem *modem,
+                     MMModemState old_state,
+                     MMModemState new_state,
+                     MMModemStateChangeReason reason,
+                     NMModemBroadband *self)
+{
+       gboolean old;
+       gboolean new;
+
+       nm_log_info (LOGD_MB, "(%s) state changed, '%s' --> '%s' (reason: 
%s)\n",
+                    nm_modem_get_uid (NM_MODEM (self)),
+                    mm_modem_state_get_string (old_state),
+                    mm_modem_state_get_string (new_state),
+                    mm_modem_state_change_reason_get_string (reason));
+
+       old = nm_modem_get_mm_enabled (NM_MODEM (self));
+       new = (mm_modem_get_state (self->priv->modem_iface) >= 
MM_MODEM_STATE_ENABLED);
+       if (old != new)
+               g_object_set (self,
+                             NM_MODEM_ENABLED, new,
+                             NULL);
+
+       old = nm_modem_get_mm_connected (NM_MODEM (self));
+       new = (mm_modem_get_state (self->priv->modem_iface) >= 
MM_MODEM_STATE_CONNECTED);
+       if (old != new)
+               g_object_set (self,
+                             NM_MODEM_CONNECTED, new,
+                             NULL);
+}
+
+/*****************************************************************************/
+
+NMModem *
+nm_modem_broadband_new (MMObject *modem_object)
+{
+       NMModem *self;
+       MMModem *modem_iface;
+       gchar *uid;
+       const gchar *path;
+       const gchar *aux;
+
+       /* Ensure we have the 'Modem' interface at least */
+       modem_iface = mm_object_peek_modem (modem_object);
+       g_return_val_if_fail (!!modem_iface, NULL);
+
+       /* Uid is just a unique string we build for logs and such */
+       path = mm_object_get_path (modem_object);
+       g_assert (g_str_has_prefix (path, MM_DBUS_MODEM_PREFIX));
+       aux = &path[strlen (MM_DBUS_MODEM_PREFIX)];
+       while (*aux == '/')
+               aux++;
+       uid = g_strdup_printf ("modem%s", aux);
+
+       /* If the modem is in 'FAILED' state we cannot do anything with it.
+        * This happens when a severe error happened when trying to initialize 
it,
+        * like missing SIM. */
+       if (mm_modem_get_state (modem_iface) == MM_MODEM_STATE_FAILED) {
+               nm_log_warn (LOGD_MB, "(%s): unusable modem detected", uid);
+               g_free (uid);
+               return NULL;
+       }
+
+       self = (NMModem *) g_object_new (NM_TYPE_MODEM_BROADBAND,
+                                        NM_MODEM_PATH, path,
+                                        NM_MODEM_UID, uid,
+                                        NM_MODEM_CONTROL_PORT, 
mm_modem_get_primary_port (modem_iface),
+                                        NM_MODEM_DATA_PORT, NULL, /* We don't 
know it until bearer created */
+                                        NM_MODEM_BROADBAND_MODEM, modem_object,
+                                        NULL);
+       g_free (uid);
+       return self;
+}
+
+static void
+nm_modem_broadband_init (NMModemBroadband *self)
+{
+       self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+                                                 NM_TYPE_MODEM_BROADBAND,
+                                                 NMModemBroadbandPrivate);
+}
+
+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_assert (self->priv->modem_iface != NULL);
+               g_signal_connect (self->priv->modem_iface,
+                          "state-changed",
+                          G_CALLBACK (modem_state_changed),
+                          self);
+
+               g_object_set (object,
+                             NM_MODEM_ENABLED, (mm_modem_get_state 
(self->priv->modem_iface) >= MM_MODEM_STATE_ENABLED),
+                             NM_MODEM_CONNECTED, (mm_modem_get_state 
(self->priv->modem_iface) >= MM_MODEM_STATE_CONNECTED),
+                             NULL);
+
+               /* 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
+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
+dispose (GObject *object)
+{
+       NMModemBroadband *self = NM_MODEM_BROADBAND (object);
+
+       g_clear_object (&self->priv->ipv4_config);
+       g_clear_object (&self->priv->ipv6_config);
+       g_clear_object (&self->priv->bearer);
+       g_clear_object (&self->priv->modem_iface);
+       g_clear_object (&self->priv->simple_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);
+
+       g_type_class_add_private (object_class, sizeof 
(NMModemBroadbandPrivate));
+
+       /* Virtual methods */
+       object_class->dispose = dispose;
+       object_class->get_property = get_property;
+       object_class->set_property = set_property;
+
+       modem_class->static_stage3_ip4_config_start = 
static_stage3_ip4_config_start;
+       modem_class->disconnect = disconnect;
+       modem_class->deactivate = deactivate;
+       modem_class->set_mm_enabled = set_mm_enabled;
+       modem_class->get_user_pass = get_user_pass;
+       modem_class->get_setting_name = get_setting_name;
+       modem_class->get_best_auto_connection = get_best_auto_connection;
+       modem_class->check_connection_compatible = check_connection_compatible;
+       modem_class->complete_connection = complete_connection;
+       modem_class->act_stage1_prepare = act_stage1_prepare;
+
+       /* Properties */
+       g_object_class_install_property
+               (object_class, PROP_MODEM,
+                g_param_spec_object (NM_MODEM_BROADBAND_MODEM,
+                                     "Modem",
+                                     "Broadband modem object",
+                                     MM_GDBUS_TYPE_OBJECT,
+                                     G_PARAM_READWRITE | 
G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/src/modem-manager/nm-modem-broadband.h 
b/src/modem-manager/nm-modem-broadband.h
new file mode 100644
index 0000000..dafcce6
--- /dev/null
+++ b/src/modem-manager/nm-modem-broadband.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 - Aleksander Morgado <[email protected]>
+ */
+
+#ifndef NM_MODEM_BROADBAND_H
+#define NM_MODEM_BROADBAND_H
+
+#include <libmm-glib.h>
+
+#include <dbus/dbus-glib.h>
+#include <glib-object.h>
+#include "nm-modem.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_MODEM_BROADBAND            (nm_modem_broadband_get_type ())
+#define NM_MODEM_BROADBAND(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
NM_TYPE_MODEM_BROADBAND, NMModemBroadband))
+#define NM_MODEM_BROADBAND_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass))
+#define NM_IS_MODEM_BROADBAND(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
NM_TYPE_MODEM_BROADBAND))
+#define NM_IS_MODEM_BROADBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
NM_TYPE_MODEM_BROADBAND))
+#define NM_MODEM_BROADBAND_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
NM_TYPE_MODEM_BROADBAND, NMModemBroadbandClass))
+
+#define NM_MODEM_BROADBAND_MODEM "modem"
+
+typedef struct _NMModemBroadband        NMModemBroadband;
+typedef struct _NMModemBroadbandClass   NMModemBroadbandClass;
+typedef struct _NMModemBroadbandPrivate NMModemBroadbandPrivate;
+
+typedef enum {
+       NM_MODEM_BROADBAND_ERROR_CONNECTION_NOT_GSM,      /*< 
nick=ConnectionNotGsm >*/
+       NM_MODEM_BROADBAND_ERROR_CONNECTION_NOT_CDMA,     /*< 
nick=ConnectionNotCdma >*/
+       NM_MODEM_BROADBAND_ERROR_CONNECTION_INVALID,      /*< 
nick=ConnectionInvalid >*/
+       NM_MODEM_BROADBAND_ERROR_CONNECTION_INCOMPATIBLE, /*< 
nick=ConnectionIncompatible >*/
+} NMModemBroadbandError;
+
+struct _NMModemBroadband {
+       NMModem parent;
+       NMModemBroadbandPrivate *priv;
+};
+
+struct _NMModemBroadbandClass {
+       NMModemClass parent;
+};
+
+GType nm_modem_broadband_get_type (void);
+
+NMModem *nm_modem_broadband_new (MMObject *modem_object);
+
+void nm_modem_broadband_get_capabilities (NMModemBroadband *self,
+                                          NMDeviceModemCapabilities 
*modem_caps,
+                                          NMDeviceModemCapabilities 
*current_caps);
+
+G_END_DECLS
+
+#endif /* NM_MODEM_BROADBAND_H */
-- 
1.7.11.7

_______________________________________________
networkmanager-list mailing list
[email protected]
https://mail.gnome.org/mailman/listinfo/networkmanager-list

Reply via email to