diff options
Diffstat (limited to 'src/modem-manager')
-rw-r--r-- | src/modem-manager/Makefile.am | 45 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-cdma.c | 264 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-cdma.h | 36 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-gsm-hso.c | 348 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-gsm-hso.h | 33 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-gsm-mbm.c | 261 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-gsm-mbm.h | 55 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-gsm.c | 354 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-gsm.h | 36 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-manager.c | 395 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-manager.h | 35 | ||||
-rw-r--r-- | src/modem-manager/nm-modem-types.h | 90 | ||||
-rw-r--r-- | src/modem-manager/nm-modem.c | 457 | ||||
-rw-r--r-- | src/modem-manager/nm-modem.h | 55 |
14 files changed, 2464 insertions, 0 deletions
diff --git a/src/modem-manager/Makefile.am b/src/modem-manager/Makefile.am new file mode 100644 index 0000000000..75ab97e041 --- /dev/null +++ b/src/modem-manager/Makefile.am @@ -0,0 +1,45 @@ +INCLUDES = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/include \ + -I${top_srcdir}/libnm-util \ + -I${top_builddir}/marshallers + +noinst_LTLIBRARIES = libmodem-manager.la + +libmodem_manager_la_SOURCES = \ + nm-modem-cdma.c \ + nm-modem-cdma.h \ + nm-modem-gsm.c \ + nm-modem-gsm.h \ + nm-modem-gsm-hso.c \ + nm-modem-gsm-hso.h \ + nm-modem-gsm-mbm.c \ + nm-modem-gsm-mbm.h \ + nm-modem.c \ + nm-modem.h \ + nm-modem-manager.h \ + nm-modem-manager.c \ + nm-modem-types.h + +libmodem_manager_la_CPPFLAGS = \ + $(DBUS_CFLAGS) + +libmodem_manager_la_LIBADD = \ + $(DBUS_LIBS) \ + $(top_builddir)/marshallers/libmarshallers.la + +nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml + dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $< + +nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml + dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $< + +nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml + dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $< + +BUILT_SOURCES = \ + nm-device-cdma-glue.h \ + nm-device-gsm-glue.h \ + nm-serial-device-glue.h + +CLEANFILES = $(BUILT_SOURCES) diff --git a/src/modem-manager/nm-modem-cdma.c b/src/modem-manager/nm-modem-cdma.c new file mode 100644 index 0000000000..1d426962d0 --- /dev/null +++ b/src/modem-manager/nm-modem-cdma.c @@ -0,0 +1,264 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#include <string.h> + +#include "nm-modem-cdma.h" +#include "nm-modem-types.h" +#include "nm-device-interface.h" +#include "nm-device-private.h" +#include "nm-dbus-manager.h" +#include "nm-setting-connection.h" +#include "nm-setting-cdma.h" +#include "nm-utils.h" + +#include "nm-device-cdma-glue.h" + +G_DEFINE_TYPE (NMModemCdma, nm_modem_cdma, NM_TYPE_MODEM) + +#define NM_MODEM_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_CDMA, NMModemCdmaPrivate)) + +enum { + MODEM_STATE_BEGIN, + MODEM_STATE_ENABLE, + MODEM_STATE_CONNECT +}; + +typedef struct { + int modem_state; +} NMModemCdmaPrivate; + +enum { + SIGNAL_QUALITY, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +NMDevice * +nm_modem_cdma_new (const char *path, + const char *data_device, + const char *driver) +{ + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (data_device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_MODEM_CDMA, + NM_DEVICE_INTERFACE_UDI, path, + NM_DEVICE_INTERFACE_IFACE, data_device, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_MANAGED, TRUE, + NM_MODEM_PATH, path, + NULL); +} + +static NMSetting * +get_setting (NMModemCdma *self, GType setting_type) +{ + NMActRequest *req; + NMSetting *setting = NULL; + + req = nm_device_get_act_request (NM_DEVICE (self)); + if (req) { + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + if (connection) + setting = nm_connection_get_setting (connection, setting_type); + } + + return setting; +} + +static void +state_machine (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemCdma *modem = NM_MODEM_CDMA (user_data); + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (modem); + NMSettingCdma *setting; + GError *error = NULL; + + setting = NM_SETTING_CDMA (get_setting (modem, NM_TYPE_SETTING_CDMA)); + + if (call_id) + dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID); + + if (error) { + nm_warning ("CDMA modem connection failed: %s", error->message); + nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); + return; + } + + switch (priv->modem_state) { + case MODEM_STATE_BEGIN: + priv->modem_state = MODEM_STATE_ENABLE; + dbus_g_proxy_begin_call (nm_modem_get_proxy (NM_MODEM (modem), NULL), + "Enable", state_machine, + modem, NULL, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID); + break; + case MODEM_STATE_ENABLE: + priv->modem_state = MODEM_STATE_CONNECT; + dbus_g_proxy_begin_call (nm_modem_get_proxy (NM_MODEM (modem), NULL), + "Connect", state_machine, + modem, NULL, + G_TYPE_STRING, nm_setting_cdma_get_number (setting), + G_TYPE_INVALID); + break; + case MODEM_STATE_CONNECT: + nm_device_activate_schedule_stage2_device_config (NM_DEVICE (modem)); + break; + default: + nm_warning ("Invalid modem state %d", priv->modem_state); + nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); + break; + } +} + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) +{ + NMModemCdmaPrivate *priv = NM_MODEM_CDMA_GET_PRIVATE (device); + + priv->modem_state = MODEM_STATE_BEGIN; + state_machine (NULL, NULL, device); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMConnection * +real_get_best_auto_connection (NMDevice *dev, + GSList *connections, + char **specific_object) +{ + GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_CDMA_SETTING_NAME)) + continue; + + return connection; + } + return NULL; +} + +static void +real_connection_secrets_updated (NMDevice *dev, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMActRequest *req; + gboolean found = FALSE; + GSList *iter; + + if (caller == SECRETS_CALLER_PPP) { + NMPPPManager *ppp_manager; + NMSettingCdma *s_cdma = NULL; + + ppp_manager = nm_modem_get_ppp_manager (NM_MODEM (dev)); + g_return_if_fail (ppp_manager != NULL); + + s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); + if (!s_cdma) { + /* Shouldn't ever happen */ + nm_ppp_manager_update_secrets (ppp_manager, + nm_device_get_iface (dev), + NULL, + NULL, + "missing CDMA setting; no secrets could be found."); + } else { + const char *username = nm_setting_cdma_get_username (s_cdma); + const char *password = nm_setting_cdma_get_password (s_cdma); + + nm_ppp_manager_update_secrets (ppp_manager, + nm_device_get_iface (dev), + username ? username : "", + password ? password : "", + NULL); + } + return; + } + + g_return_if_fail (caller == SECRETS_CALLER_CDMA); + g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); + + for (iter = updated_settings; iter; iter = g_slist_next (iter)) { + const char *setting_name = (const char *) iter->data; + + if (!strcmp (setting_name, NM_SETTING_CDMA_SETTING_NAME)) + found = TRUE; + else + nm_warning ("Ignoring updated secrets for setting '%s'.", setting_name); + } + + if (!found) + return; + + req = nm_device_get_act_request (dev); + g_assert (req); + + g_return_if_fail (nm_act_request_get_connection (req) == connection); + + nm_device_activate_schedule_stage1_device_prepare (dev); +} + +static const char * +real_get_ppp_name (NMModem *device, NMConnection *connection) +{ + NMSettingCdma *s_cdma; + + s_cdma = (NMSettingCdma *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CDMA); + g_assert (s_cdma); + + return nm_setting_cdma_get_username (s_cdma); +} + +/*****************************************************************************/ + +static void +nm_modem_cdma_init (NMModemCdma *self) +{ + nm_device_set_device_type (NM_DEVICE (self), NM_DEVICE_TYPE_CDMA); +} + +static void +nm_modem_cdma_class_init (NMModemCdmaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemCdmaPrivate)); + + /* Virtual methods */ + device_class->get_best_auto_connection = real_get_best_auto_connection; + device_class->connection_secrets_updated = real_connection_secrets_updated; + device_class->act_stage1_prepare = real_act_stage1_prepare; + modem_class->get_ppp_name = real_get_ppp_name; + + /* Signals */ + signals[SIGNAL_QUALITY] = + g_signal_new ("signal-quality", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemCdmaClass, signal_quality), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, + G_TYPE_UINT); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_cdma_object_info); +} diff --git a/src/modem-manager/nm-modem-cdma.h b/src/modem-manager/nm-modem-cdma.h new file mode 100644 index 0000000000..a1f3bf5459 --- /dev/null +++ b/src/modem-manager/nm-modem-cdma.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#ifndef NM_MODEM_CDMA_H +#define NM_MODEM_CDMA_H + +#include <nm-modem.h> + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_CDMA (nm_modem_cdma_get_type ()) +#define NM_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_CDMA, NMModemCdma)) +#define NM_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_CDMA, NMModemCdmaClass)) +#define NM_IS_MODEM_CDMA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_CDMA)) +#define NM_IS_MODEM_CDMA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_CDMA)) +#define NM_MODEM_CDMA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_CDMA, NMModemCdmaClass)) + +typedef struct { + NMModem parent; +} NMModemCdma; + +typedef struct { + NMModemClass parent; + + /* Signals */ + void (*signal_quality) (NMModemCdma *self, guint32 quality); +} NMModemCdmaClass; + +GType nm_modem_cdma_get_type (void); + +NMDevice *nm_modem_cdma_new (const char *path, + const char *data_device, + const char *driver); + +G_END_DECLS + +#endif /* NM_MODEM_CDMA_H */ diff --git a/src/modem-manager/nm-modem-gsm-hso.c b/src/modem-manager/nm-modem-gsm-hso.c new file mode 100644 index 0000000000..343bfa3f24 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm-hso.c @@ -0,0 +1,348 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#include "nm-modem-gsm-hso.h" +#include "nm-device-private.h" +#include "nm-device-interface.h" +#include "NetworkManagerSystem.h" +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-modem-types.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMModemGsmHso, nm_modem_gsm_hso, NM_TYPE_MODEM_GSM) + +#define NM_MODEM_GSM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_GSM_HSO, NMModemGsmHsoPrivate)) + +typedef struct { + char *netdev_iface; + NMIP4Config *pending_ip4_config; +} NMModemGsmHsoPrivate; + +#define HSO_SECRETS_TRIES "gsm-secrets-tries" + +static char * +get_network_device (NMDevice *device) +{ + char *result = NULL; + GError *error = NULL; + GValue value = { 0, }; + + if (!dbus_g_proxy_call (nm_modem_get_proxy (NM_MODEM (device), "org.freedesktop.DBus.Properties"), + "Get", &error, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM_GSM_HSO, + G_TYPE_STRING, "NetworkDevice", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + nm_warning ("Could not get HSO device's network interface: %s", error->message); + g_error_free (error); + } else { + if (G_VALUE_HOLDS_STRING (&value)) + result = g_value_dup_string (&value); + else + nm_warning ("Could not get HSO device's network interface: wrong type '%s'", + G_VALUE_TYPE_NAME (&value)); + + g_value_unset (&value); + } + + return result; +} + +NMDevice * +nm_modem_gsm_hso_new (const char *path, + const char *data_device, + const char *driver) +{ + NMDevice *device; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (data_device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + device = (NMDevice *) g_object_new (NM_TYPE_MODEM_GSM_HSO, + NM_DEVICE_INTERFACE_UDI, path, + NM_DEVICE_INTERFACE_IFACE, data_device, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_MANAGED, TRUE, + NM_MODEM_PATH, path, + NULL); + + if (device) { + NMModemGsmHsoPrivate *priv; + + priv = NM_MODEM_GSM_HSO_GET_PRIVATE (device); + priv->netdev_iface = get_network_device (device); + if (!priv->netdev_iface) { + g_object_unref (device); + device = NULL; + } + } + + return device; +} + +/*****************************************************************************/ + +static NMSetting * +get_setting (NMModemGsmHso *modem, GType setting_type) +{ + NMActRequest *req; + NMSetting *setting = NULL; + + req = nm_device_get_act_request (NM_DEVICE (modem)); + if (req) { + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + if (connection) + setting = nm_connection_get_setting (connection, setting_type); + } + + return setting; +} + +static void +hso_auth_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) + nm_device_activate_schedule_stage3_ip_config_start (device); + else { + nm_warning ("Authentication failed: %s", error->message); + g_error_free (error); + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED); + } +} + +static void +do_hso_auth (NMModemGsmHso *device) +{ + NMSettingGsm *s_gsm; + const char *username; + const char *password; + + s_gsm = NM_SETTING_GSM (get_setting (device, NM_TYPE_SETTING_GSM)); + username = nm_setting_gsm_get_username (s_gsm); + password = nm_setting_gsm_get_password (s_gsm); + + dbus_g_proxy_begin_call (nm_modem_get_proxy (NM_MODEM (device), MM_DBUS_INTERFACE_MODEM_GSM_HSO), + "Authenticate", hso_auth_done, + device, NULL, + G_TYPE_STRING, username ? username : "", + G_TYPE_STRING, password ? password : "", + G_TYPE_INVALID); +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMActRequest *req; + NMConnection *connection; + const char *setting_name; + GPtrArray *hints = NULL; + const char *hint1 = NULL, *hint2 = NULL; + guint32 tries; + + req = nm_device_get_act_request (device); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + setting_name = nm_connection_need_secrets (connection, &hints); + if (!setting_name) { + do_hso_auth (NM_MODEM_GSM_HSO (device)); + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + if (hints) { + if (hints->len > 0) + hint1 = g_ptr_array_index (hints, 0); + if (hints->len > 1) + hint2 = g_ptr_array_index (hints, 1); + } + + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + + tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), HSO_SECRETS_TRIES)); + nm_act_request_request_connection_secrets (req, + setting_name, + tries ? TRUE : FALSE, + SECRETS_CALLER_HSO_GSM, + hint1, + hint2); + g_object_set_data (G_OBJECT (connection), HSO_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); + + if (hints) + g_ptr_array_free (hints, TRUE); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static void +get_ip4_config_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + guint32 ip4_address; + GArray *dns_array; + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, + G_TYPE_UINT, &ip4_address, + DBUS_TYPE_G_UINT_ARRAY, &dns_array, + G_TYPE_INVALID)) { + + NMModemGsmHsoPrivate *priv = NM_MODEM_GSM_HSO_GET_PRIVATE (device); + NMIP4Address *addr; + int i; + + addr = nm_ip4_address_new (); + nm_ip4_address_set_address (addr, ip4_address); + nm_ip4_address_set_prefix (addr, 32); + + priv->pending_ip4_config = nm_ip4_config_new (); + nm_ip4_config_take_address (priv->pending_ip4_config, addr); + + for (i = 0; i < dns_array->len; i++) + nm_ip4_config_add_nameserver (priv->pending_ip4_config, + g_array_index (dns_array, guint32, i)); + + nm_device_activate_schedule_stage4_ip_config_get (device); + } else { + nm_warning ("Retrieving IP4 configuration failed: %s", error->message); + g_error_free (error); + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + } +} + +static NMActStageReturn +real_act_stage3_ip_config_start (NMDevice *device, NMDeviceStateReason *reason) +{ + dbus_g_proxy_begin_call (nm_modem_get_proxy (NM_MODEM (device), MM_DBUS_INTERFACE_MODEM_GSM_HSO), + "GetIP4Config", get_ip4_config_done, + device, NULL, + G_TYPE_INVALID); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *device, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMModemGsmHso *self = NM_MODEM_GSM_HSO (device); + NMModemGsmHsoPrivate *priv = NM_MODEM_GSM_HSO_GET_PRIVATE (self); + gboolean no_firmware = FALSE; + + nm_device_set_ip_iface (device, priv->netdev_iface); + if (!nm_device_hw_bring_up (device, TRUE, &no_firmware)) { + if (no_firmware) + *reason = NM_DEVICE_STATE_REASON_FIRMWARE_MISSING; + else + *reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED; + return NM_ACT_STAGE_RETURN_FAILURE; + } + + *config = priv->pending_ip4_config; + priv->pending_ip4_config = NULL; + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +real_deactivate (NMDevice *device) +{ + NMModemGsmHsoPrivate *priv = NM_MODEM_GSM_HSO_GET_PRIVATE (device); + + if (priv->pending_ip4_config) { + g_object_unref (priv->pending_ip4_config); + priv->pending_ip4_config = NULL; + } + + if (priv->netdev_iface) { + nm_system_device_flush_ip4_routes_with_iface (priv->netdev_iface); + nm_system_device_flush_ip4_addresses_with_iface (priv->netdev_iface); + nm_system_device_set_up_down_with_iface (priv->netdev_iface, FALSE, NULL); + } + nm_device_set_ip_iface (device, NULL); + + if (NM_DEVICE_CLASS (nm_modem_gsm_hso_parent_class)->deactivate) + NM_DEVICE_CLASS (nm_modem_gsm_hso_parent_class)->deactivate (device); +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + NMModemGsmHsoPrivate *priv = NM_MODEM_GSM_HSO_GET_PRIVATE (device); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) + return nm_system_device_is_up_with_iface (priv->netdev_iface); + + return TRUE; +} + +static gboolean +real_hw_bring_up (NMDevice *device, gboolean *no_firmware) +{ + NMModemGsmHsoPrivate *priv = NM_MODEM_GSM_HSO_GET_PRIVATE (device); + NMDeviceState state; + + state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); + if (priv->pending_ip4_config || state == NM_DEVICE_STATE_IP_CONFIG || state == NM_DEVICE_STATE_ACTIVATED) + return nm_system_device_set_up_down_with_iface (priv->netdev_iface, TRUE, no_firmware); + + return TRUE; +} + +static void +real_connect (NMModem *modem, const char *number) +{ + nm_device_activate_schedule_stage2_device_config (NM_DEVICE (modem)); +} + +/*****************************************************************************/ + +static void +nm_modem_gsm_hso_init (NMModemGsmHso *self) +{ +} + +static void +finalize (GObject *object) +{ + NMModemGsmHsoPrivate *priv = NM_MODEM_GSM_HSO_GET_PRIVATE (object); + + g_free (priv->netdev_iface); + + G_OBJECT_CLASS (nm_modem_gsm_hso_parent_class)->finalize (object); +} + +static void +nm_modem_gsm_hso_class_init (NMModemGsmHsoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemGsmHsoPrivate)); + + object_class->finalize = finalize; + + device_class->act_stage2_config = real_act_stage2_config; + device_class->act_stage3_ip_config_start = real_act_stage3_ip_config_start; + device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + device_class->deactivate = real_deactivate; + device_class->hw_is_up = real_hw_is_up; + device_class->hw_bring_up = real_hw_bring_up; + + modem_class->connect = real_connect; +} diff --git a/src/modem-manager/nm-modem-gsm-hso.h b/src/modem-manager/nm-modem-gsm-hso.h new file mode 100644 index 0000000000..9b67bee921 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm-hso.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#ifndef NM_MODEM_GSM_HSO_H +#define NM_MODEM_GSM_HSO_H + +#include <nm-modem-gsm.h> + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_GSM_HSO (nm_modem_gsm_hso_get_type ()) +#define NM_MODEM_GSM_HSO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_GSM_HSO, NMModemGsmHso)) +#define NM_MODEM_GSM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_GSM_HSO, NMModemGsmHsoClass)) +#define NM_IS_MODEM_GSM_HSO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_GSM_HSO)) +#define NM_IS_MODEM_GSM_HSO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_GSM_HSO)) +#define NM_MODEM_GSM_HSO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_GSM_HSO, NMModemGsmHsoClass)) + +typedef struct { + NMModemGsm parent; +} NMModemGsmHso; + +typedef struct { + NMModemGsmClass parent; +} NMModemGsmHsoClass; + +GType nm_modem_gsm_hso_get_type (void); + +NMDevice *nm_modem_gsm_hso_new (const char *path, + const char *data_device, + const char *driver); + +G_END_DECLS + +#endif /* NM_MODEM_GSM_HSO_H */ diff --git a/src/modem-manager/nm-modem-gsm-mbm.c b/src/modem-manager/nm-modem-gsm-mbm.c new file mode 100644 index 0000000000..adac6f7552 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm-mbm.c @@ -0,0 +1,261 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + Additions to NetworkManager, network-manager-applet and modemmanager + for supporting Ericsson modules like F3507g. + + Author: Per Hallsmark <per@hallsmark.se> + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "nm-modem-gsm-mbm.h" +#include "nm-device-private.h" +#include "nm-device-interface.h" +#include "NetworkManagerSystem.h" +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-modem-types.h" +#include "nm-utils.h" + +G_DEFINE_TYPE (NMModemGsmMbm, nm_modem_gsm_mbm, NM_TYPE_MODEM_GSM) + +#define NM_MODEM_GSM_MBM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_GSM_MBM, NMModemGsmMbmPrivate)) + +typedef struct { + char *netdev_iface; + NMIP4Config *pending_ip4_config; +} NMModemGsmMbmPrivate; + +#define MBM_SECRETS_TRIES "gsm-secrets-tries" + +static char * +get_network_device (NMDevice *device) +{ + char *result = NULL; + GError *error = NULL; + GValue value = { 0, }; + + if (!dbus_g_proxy_call (nm_modem_get_proxy (NM_MODEM (device), "org.freedesktop.DBus.Properties"), + "Get", &error, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM_GSM_MBM, + G_TYPE_STRING, "NetworkDevice", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + nm_warning ("Could not get MBM device's network interface: %s", error->message); + g_error_free (error); + } else { + if (G_VALUE_HOLDS_STRING (&value)) + result = g_value_dup_string (&value); + else + nm_warning ("Could not get MBM device's network interface: wrong type '%s'", + G_VALUE_TYPE_NAME (&value)); + + g_value_unset (&value); + } + + return result; +} + +NMDevice * +nm_modem_gsm_mbm_new (const char *path, + const char *data_device, + const char *driver) +{ + NMDevice *device; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (data_device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + device = (NMDevice *) g_object_new (NM_TYPE_MODEM_GSM_MBM, + NM_DEVICE_INTERFACE_UDI, path, + NM_DEVICE_INTERFACE_IFACE, data_device, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_MANAGED, TRUE, + NM_MODEM_PATH, path, + NULL); + + if (device) { + NMModemGsmMbmPrivate *priv; + + priv = NM_MODEM_GSM_MBM_GET_PRIVATE (device); + priv->netdev_iface = get_network_device (device); + if (!priv->netdev_iface) { + g_object_unref (device); + device = NULL; + } + } + + return device; +} + +/*****************************************************************************/ + +#if 0 +static NMSetting * +get_setting (NMModemGsmMbm *modem, GType setting_type) +{ + NMActRequest *req; + NMSetting *setting = NULL; + + req = nm_device_get_act_request (NM_DEVICE (modem)); + if (req) { + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + if (connection) + setting = nm_connection_get_setting (connection, setting_type); + } + + return setting; +} +#endif + +#if 0 +static NMActStageReturn +real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMActRequest *req; + NMConnection *connection; + const char *setting_name; + GPtrArray *hints = NULL; + const char *hint1 = NULL, *hint2 = NULL; + guint32 tries; + + req = nm_device_get_act_request (device); + g_assert (req); + connection = nm_act_request_get_connection (req); + g_assert (connection); + + setting_name = nm_connection_need_secrets (connection, &hints); + if (!setting_name) { + // do_mbm_auth (NM_MODEM_GSM_MBM (device)); + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + if (hints) { + if (hints->len > 0) + hint1 = g_ptr_array_index (hints, 0); + if (hints->len > 1) + hint2 = g_ptr_array_index (hints, 1); + } + + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + + tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), MBM_SECRETS_TRIES)); + nm_act_request_request_connection_secrets (req, + setting_name, + tries ? TRUE : FALSE, + SECRETS_CALLER_MBM_GSM, + hint1, + hint2); + g_object_set_data (G_OBJECT (connection), MBM_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); + + if (hints) + g_ptr_array_free (hints, TRUE); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} +#endif + +static void +real_deactivate (NMDevice *device) +{ + NMModemGsmMbmPrivate *priv = NM_MODEM_GSM_MBM_GET_PRIVATE (device); + + if (priv->pending_ip4_config) { + g_object_unref (priv->pending_ip4_config); + priv->pending_ip4_config = NULL; + } + + if (priv->netdev_iface) { + nm_system_device_flush_ip4_routes_with_iface (priv->netdev_iface); + nm_system_device_flush_ip4_addresses_with_iface (priv->netdev_iface); + nm_system_device_set_up_down_with_iface (priv->netdev_iface, FALSE, NULL); + } + nm_device_set_ip_iface (device, NULL); + + if (NM_DEVICE_CLASS (nm_modem_gsm_mbm_parent_class)->deactivate) + NM_DEVICE_CLASS (nm_modem_gsm_mbm_parent_class)->deactivate (device); +} + +static gboolean +real_hw_is_up (NMDevice *device) +{ + NMModemGsmMbmPrivate *priv = NM_MODEM_GSM_MBM_GET_PRIVATE (device); + + if (priv->netdev_iface) + return nm_system_device_is_up_with_iface (priv->netdev_iface); + + return TRUE; +} + +static gboolean +real_hw_bring_up (NMDevice *device, gboolean *no_firmware) +{ + NMModemGsmMbmPrivate *priv = NM_MODEM_GSM_MBM_GET_PRIVATE (device); + + if (priv->netdev_iface) + return nm_system_device_set_up_down_with_iface (priv->netdev_iface, TRUE, no_firmware); + + return TRUE; +} + +static void +real_connect (NMModem *modem, const char *number) +{ + nm_device_activate_schedule_stage2_device_config (NM_DEVICE (modem)); +} + +/*****************************************************************************/ + +static void +nm_modem_gsm_mbm_init (NMModemGsmMbm *self) +{ +} + +static void +finalize (GObject *object) +{ + NMModemGsmMbmPrivate *priv = NM_MODEM_GSM_MBM_GET_PRIVATE (object); + + g_free (priv->netdev_iface); + + G_OBJECT_CLASS (nm_modem_gsm_mbm_parent_class)->finalize (object); +} + +static void +nm_modem_gsm_mbm_class_init (NMModemGsmMbmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemGsmMbmPrivate)); + + object_class->finalize = finalize; + +#if 0 + device_class->act_stage2_config = real_act_stage2_config; +#endif + device_class->deactivate = real_deactivate; + device_class->hw_is_up = real_hw_is_up; + device_class->hw_bring_up = real_hw_bring_up; + + modem_class->connect = real_connect; +} diff --git a/src/modem-manager/nm-modem-gsm-mbm.h b/src/modem-manager/nm-modem-gsm-mbm.h new file mode 100644 index 0000000000..9bd4b3a7da --- /dev/null +++ b/src/modem-manager/nm-modem-gsm-mbm.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + Additions to NetworkManager, network-manager-applet and modemmanager + for supporting Ericsson modules like F3507g. + + Author: Per Hallsmark <per@hallsmark.se> + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef NM_MODEM_GSM_MBM_H +#define NM_MODEM_GSM_MBM_H + +#include <nm-modem-gsm.h> + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_GSM_MBM (nm_modem_gsm_mbm_get_type ()) +#define NM_MODEM_GSM_MBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_GSM_MBM, NMModemGsmMbm)) +#define NM_MODEM_GSM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_GSM_MBM, NMModemGsmMbmClass)) +#define NM_IS_MODEM_GSM_MBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_GSM_MBM)) +#define NM_IS_MODEM_GSM_MBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_GSM_MBM)) +#define NM_MODEM_GSM_MBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_GSM_MBM, NMModemGsmMbmClass)) + +typedef struct { + NMModemGsm parent; +} NMModemGsmMbm; + +typedef struct { + NMModemGsmClass parent; +} NMModemGsmMbmClass; + +GType nm_modem_gsm_mbm_get_type (void); + +NMDevice *nm_modem_gsm_mbm_new (const char *path, + const char *data_device, + const char *driver); + +G_END_DECLS + +#endif /* NM_MODEM_GSM_MBM_H */ diff --git a/src/modem-manager/nm-modem-gsm.c b/src/modem-manager/nm-modem-gsm.c new file mode 100644 index 0000000000..15231797e6 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm.c @@ -0,0 +1,354 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#include <string.h> +#include "nm-modem-gsm.h" +#include "nm-device-private.h" +#include "nm-device-interface.h" +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-modem-types.h" +#include "nm-utils.h" + +#include "nm-device-gsm-glue.h" + +G_DEFINE_TYPE (NMModemGsm, nm_modem_gsm, NM_TYPE_MODEM) + +#define NM_MODEM_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_GSM, NMModemGsmPrivate)) + +enum { + MODEM_STATE_BEGIN, + MODEM_STATE_ENABLE, + MODEM_STATE_SET_PIN, + MODEM_STATE_SET_APN, + MODEM_STATE_SET_BAND, + MODEM_STATE_SET_NETWORK_MODE, + MODEM_STATE_REGISTER, + MODEM_STATE_FAILED, +}; + +typedef struct { + int modem_state; +} NMModemGsmPrivate; + +NMDevice * +nm_modem_gsm_new (const char *path, + const char *data_device, + const char *driver) +{ + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (data_device != NULL, NULL); + g_return_val_if_fail (driver != NULL, NULL); + + return (NMDevice *) g_object_new (NM_TYPE_MODEM_GSM, + NM_DEVICE_INTERFACE_UDI, path, + NM_DEVICE_INTERFACE_IFACE, data_device, + NM_DEVICE_INTERFACE_DRIVER, driver, + NM_DEVICE_INTERFACE_MANAGED, TRUE, + NM_MODEM_PATH, path, + NULL); +} + +static NMSetting * +get_setting (NMModemGsm *modem, GType setting_type) +{ + NMActRequest *req; + NMSetting *setting = NULL; + + req = nm_device_get_act_request (NM_DEVICE (modem)); + if (req) { + NMConnection *connection; + + connection = nm_act_request_get_connection (req); + if (connection) + setting = nm_connection_get_setting (connection, setting_type); + } + + return setting; +} + +#define get_proxy(dev,iface) (nm_modem_get_proxy(NM_MODEM (dev), iface)) + +static void +state_machine (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMModemGsm *modem = NM_MODEM_GSM (user_data); + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (modem); + NMSettingGsm *setting; + const char *secret = NULL; + const char *secret_name = NULL; + const char *str; + GError *error = NULL; + int i; + gboolean retry_secret = FALSE; + + setting = NM_SETTING_GSM (get_setting (modem, NM_TYPE_SETTING_GSM)); + + if (call_id) + dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID); + + if (error) { + g_debug ("%s", dbus_g_error_get_name (error)); + + if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PIN)) { + secret = nm_setting_gsm_get_pin (setting); + secret_name = NM_SETTING_GSM_PIN; + priv->modem_state = MODEM_STATE_SET_PIN; + } else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_PUK)) { + secret = nm_setting_gsm_get_puk (setting); + secret_name = NM_SETTING_GSM_PUK; + priv->modem_state = MODEM_STATE_SET_PIN; + } else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_SIM_WRONG)) { + g_object_set (setting, NM_SETTING_GSM_PIN, NULL, NULL); + secret_name = NM_SETTING_GSM_PIN; + retry_secret = TRUE; + priv->modem_state = MODEM_STATE_SET_PIN; + } + + /* FIXME: Hacks to ignore failures of setting band and network mode for now + since only Huawei module supports it. Remove when ModemManager rules. + */ + else if (dbus_g_error_has_name (error, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED) && + (priv->modem_state == MODEM_STATE_SET_BAND || + priv->modem_state == MODEM_STATE_SET_NETWORK_MODE)) { + + nm_warning ("Modem does not support setting %s, ignoring", + priv->modem_state == MODEM_STATE_SET_BAND ? "band" : "network mode"); + } else { + priv->modem_state = MODEM_STATE_FAILED; + nm_warning ("GSM modem connection failed: %s", error->message); + } + + g_error_free (error); + } + + again: + + switch (priv->modem_state) { + case MODEM_STATE_BEGIN: + priv->modem_state = MODEM_STATE_ENABLE; + dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM), + "Enable", state_machine, + modem, NULL, + G_TYPE_BOOLEAN, TRUE, + G_TYPE_INVALID); + break; + + case MODEM_STATE_SET_PIN: + if (secret) { + priv->modem_state = MODEM_STATE_ENABLE; + dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_CARD), + "SendPin", state_machine, + modem, NULL, + G_TYPE_STRING, secret, + G_TYPE_INVALID); + } else { + nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + nm_act_request_request_connection_secrets (nm_device_get_act_request (NM_DEVICE (modem)), + NM_SETTING_GSM_SETTING_NAME, + retry_secret, + SECRETS_CALLER_GSM, + secret_name, + NULL); + + } + break; + + case MODEM_STATE_ENABLE: + priv->modem_state = MODEM_STATE_SET_APN; + str = nm_setting_gsm_get_apn (setting); + + if (str) + dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), + "SetApn", state_machine, + modem, NULL, + G_TYPE_STRING, str, + G_TYPE_INVALID); + else + goto again; + + break; + case MODEM_STATE_SET_APN: + priv->modem_state = MODEM_STATE_SET_BAND; + i = nm_setting_gsm_get_band (setting); + + if (i) + dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), + "SetBand", state_machine, + modem, NULL, + G_TYPE_UINT, (guint32) i, + G_TYPE_INVALID); + else + goto again; + + break; + + case MODEM_STATE_SET_BAND: + priv->modem_state = MODEM_STATE_SET_NETWORK_MODE; + i = nm_setting_gsm_get_network_type (setting); + + if (i) + dbus_g_proxy_begin_call (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), + "SetNetworkMode", state_machine, + modem, NULL, + G_TYPE_UINT, (guint32) i, + G_TYPE_INVALID); + else + goto again; + + break; + + case MODEM_STATE_SET_NETWORK_MODE: + priv->modem_state = MODEM_STATE_REGISTER; + + str = nm_setting_gsm_get_network_id (setting); + dbus_g_proxy_begin_call_with_timeout (get_proxy (modem, MM_DBUS_INTERFACE_MODEM_GSM_NETWORK), + "Register", state_machine, + modem, NULL, 120000, + G_TYPE_STRING, str ? str : "", + G_TYPE_INVALID); + break; + + case MODEM_STATE_REGISTER: + nm_modem_connect (NM_MODEM (modem), nm_setting_gsm_get_number (setting)); + break; + case MODEM_STATE_FAILED: + default: + nm_device_state_changed (NM_DEVICE (modem), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE); + break; + } +} + +static NMActStageReturn +real_act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) +{ + NMModemGsmPrivate *priv = NM_MODEM_GSM_GET_PRIVATE (device); + + priv->modem_state = MODEM_STATE_BEGIN; + state_machine (NULL, NULL, device); + + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +static NMConnection * +real_get_best_auto_connection (NMDevice *dev, + GSList *connections, + char **specific_object) +{ + GSList *iter; + + for (iter = connections; iter; iter = g_slist_next (iter)) { + NMConnection *connection = NM_CONNECTION (iter->data); + NMSettingConnection *s_con; + + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_con); + + if (!nm_setting_connection_get_autoconnect (s_con)) + continue; + + if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_GSM_SETTING_NAME)) + continue; + + return connection; + } + return NULL; +} + +static void +real_connection_secrets_updated (NMDevice *dev, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMActRequest *req; + gboolean found = FALSE; + GSList *iter; + + if (caller == SECRETS_CALLER_PPP) { + NMPPPManager *ppp_manager; + NMSettingGsm *s_gsm = NULL; + + ppp_manager = nm_modem_get_ppp_manager (NM_MODEM (dev)); + g_return_if_fail (ppp_manager != NULL); + + s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); + if (!s_gsm) { + /* Shouldn't ever happen */ + nm_ppp_manager_update_secrets (ppp_manager, + nm_device_get_iface (dev), + NULL, + NULL, + "missing GSM setting; no secrets could be found."); + } else { + const char *username = nm_setting_gsm_get_username (s_gsm); + const char *password = nm_setting_gsm_get_password (s_gsm); + + nm_ppp_manager_update_secrets (ppp_manager, + nm_device_get_iface (dev), + username ? username : "", + password ? password : "", + NULL); + } + return; + } + + g_return_if_fail (caller == SECRETS_CALLER_GSM); + g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); + + for (iter = updated_settings; iter; iter = g_slist_next (iter)) { + const char *setting_name = (const char *) iter->data; + + if (!strcmp (setting_name, NM_SETTING_GSM_SETTING_NAME)) + found = TRUE; + else + nm_warning ("Ignoring updated secrets for setting '%s'.", setting_name); + } + + if (!found) + return; + + req = nm_device_get_act_request (dev); + g_assert (req); + + g_return_if_fail (nm_act_request_get_connection (req) == connection); + + nm_device_activate_schedule_stage1_device_prepare (dev); +} + +static const char * +real_get_ppp_name (NMModem *device, NMConnection *connection) +{ + NMSettingGsm *s_gsm; + + s_gsm = (NMSettingGsm *) nm_connection_get_setting (connection, NM_TYPE_SETTING_GSM); + g_assert (s_gsm); + + return nm_setting_gsm_get_username (s_gsm); +} + +/*****************************************************************************/ + +static void +nm_modem_gsm_init (NMModemGsm *self) +{ + nm_device_set_device_type (NM_DEVICE (self), NM_DEVICE_TYPE_GSM); +} + +static void +nm_modem_gsm_class_init (NMModemGsmClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + NMModemClass *modem_class = NM_MODEM_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemGsmPrivate)); + + /* Virtual methods */ + device_class->get_best_auto_connection = real_get_best_auto_connection; + device_class->connection_secrets_updated = real_connection_secrets_updated; + device_class->act_stage1_prepare = real_act_stage1_prepare; + modem_class->get_ppp_name = real_get_ppp_name; + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_device_gsm_object_info); +} diff --git a/src/modem-manager/nm-modem-gsm.h b/src/modem-manager/nm-modem-gsm.h new file mode 100644 index 0000000000..50f08548e7 --- /dev/null +++ b/src/modem-manager/nm-modem-gsm.h @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#ifndef NM_MODEM_GSM_H +#define NM_MODEM_GSM_H + +#include <nm-modem.h> + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM_GSM (nm_modem_gsm_get_type ()) +#define NM_MODEM_GSM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_GSM, NMModemGsm)) +#define NM_MODEM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_GSM, NMModemGsmClass)) +#define NM_IS_MODEM_GSM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_GSM)) +#define NM_IS_MODEM_GSM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_GSM)) +#define NM_MODEM_GSM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_GSM, NMModemGsmClass)) + +typedef struct { + NMModem parent; +} NMModemGsm; + +typedef struct { + NMModemClass parent; + + /* Signals */ + void (*signal_quality) (NMModemGsm *self, guint32 quality); +} NMModemGsmClass; + +GType nm_modem_gsm_get_type (void); + +NMDevice *nm_modem_gsm_new (const char *path, + const char *data_device, + const char *driver); + +G_END_DECLS + +#endif /* NM_MODEM_GSM_H */ diff --git a/src/modem-manager/nm-modem-manager.c b/src/modem-manager/nm-modem-manager.c new file mode 100644 index 0000000000..85aaf713c4 --- /dev/null +++ b/src/modem-manager/nm-modem-manager.c @@ -0,0 +1,395 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#include <string.h> +#include "nm-modem-manager.h" +#include "nm-modem.h" +#include "nm-modem-gsm.h" +#include "nm-modem-gsm-hso.h" +#include "nm-modem-gsm-mbm.h" +#include "nm-modem-cdma.h" +#include "nm-dbus-manager.h" +#include "nm-utils.h" +#include "nm-modem-types.h" + +#define MODEM_POKE_INTERVAL 120000 + +G_DEFINE_TYPE (NMModemManager, nm_modem_manager, G_TYPE_OBJECT) + +#define NM_MODEM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM_MANAGER, NMModemManagerPrivate)) + +typedef struct { + NMDBusManager *dbus_mgr; + DBusGProxy *proxy; + GHashTable *modems; + gboolean disposed; + guint poke_id; +} NMModemManagerPrivate; + +enum { + DEVICE_ADDED, + DEVICE_REMOVED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + + +NMModemManager * +nm_modem_manager_get (void) +{ + static NMModemManager *singleton = NULL; + + if (!singleton) + singleton = NM_MODEM_MANAGER (g_object_new (NM_TYPE_MODEM_MANAGER, NULL)); + else + g_object_ref (singleton); + + g_assert (singleton); + return singleton; +} + +static gboolean +get_modem_properties (DBusGConnection *connection, + const char *path, + char **data_device, + char **driver, + guint32 *type) +{ + DBusGProxy *proxy; + GValue value = { 0 }; + GError *err = NULL; + + proxy = dbus_g_proxy_new_for_name (connection, + MM_DBUS_SERVICE, + path, + "org.freedesktop.DBus.Properties"); + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "Type", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *type = g_value_get_uint (&value); + g_value_unset (&value); + } else { + g_warning ("Could not get device type: %s", err->message); + goto out; + } + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "DataDevice", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *data_device = g_value_dup_string (&value); + g_value_unset (&value); + } else { + g_warning ("Could not get modem data device: %s", err->message); + goto out; + } + + if (dbus_g_proxy_call_with_timeout (proxy, "Get", 15000, &err, + G_TYPE_STRING, MM_DBUS_INTERFACE_MODEM, + G_TYPE_STRING, "Driver", + G_TYPE_INVALID, + G_TYPE_VALUE, &value, + G_TYPE_INVALID)) { + *driver = g_value_dup_string (&value); + g_value_unset (&value); + } else { + g_warning ("Could not get modem driver: %s", err->message); + goto out; + } + + out: + if (err) + g_error_free (err); + + g_object_unref (proxy); + + return *data_device && *driver; +} + +static void +create_modem (NMModemManager *manager, const char *path) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (manager); + NMDevice *device; + char *data_device = NULL; + char *driver = NULL; + uint modem_type = MM_MODEM_TYPE_UNKNOWN; + + if (g_hash_table_lookup (priv->modems, path)) { + nm_warning ("Modem with path %s already exists, ignoring", path); + return; + } + + if (!get_modem_properties (nm_dbus_manager_get_connection (priv->dbus_mgr), path, + &data_device, &driver, &modem_type)) + return; + + if (modem_type == MM_MODEM_TYPE_UNKNOWN) { + nm_warning ("Modem with path %s has unknown type, ignoring", path); + return; + } + + if (!driver || !strlen (driver)) { + nm_warning ("Modem with path %s has unknown driver, ignoring", path); + return; + } + + if (!data_device || !strlen (data_device)) { + nm_warning ("Modem with path %s has unknown data device, ignoring", path); + return; + } + + if (modem_type == MM_MODEM_TYPE_GSM) { + if (!strcmp (driver, "hso")) + device = nm_modem_gsm_hso_new (path, data_device, driver); + else if (!strcmp (driver, "mbm")) + device = nm_modem_gsm_mbm_new (path, data_device, driver); + else + device = nm_modem_gsm_new (path, data_device, driver); + } else if (modem_type == MM_MODEM_TYPE_CDMA) + device = nm_modem_cdma_new (path, data_device, driver); + else + g_error ("Invalid modem type"); + + g_free (data_device); + g_free (driver); + + if (device) { + g_hash_table_insert (priv->modems, g_strdup (path), device); + g_signal_emit (manager, signals[DEVICE_ADDED], 0, device); + } +} + +static void +modem_added (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + create_modem (NM_MODEM_MANAGER (user_data), path); +} + +static void +modem_removed (DBusGProxy *proxy, const char *path, gpointer user_data) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (user_data); + NMModem *modem; + + modem = (NMModem *) g_hash_table_lookup (priv->modems, path); + if (modem) { + g_signal_emit (user_data, signals[DEVICE_REMOVED], 0, modem); + g_hash_table_remove (priv->modems, path); + } +} + +static gboolean +poke_modem_cb (gpointer user_data) +{ + NMModemManager *self = NM_MODEM_MANAGER (user_data); + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + DBusGConnection *g_connection; + DBusGProxy *proxy; + + g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr); + proxy = dbus_g_proxy_new_for_name (g_connection, + MM_DBUS_SERVICE, + MM_DBUS_PATH, + MM_DBUS_INTERFACE); + + nm_info ("Trying to start the modem-manager..."); + dbus_g_proxy_call_no_reply (proxy, "EnumerateDevices", G_TYPE_INVALID); + g_object_unref (proxy); + + return TRUE; +} + +static void +enumerate_devices_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer data) +{ + NMModemManager *manager = NM_MODEM_MANAGER (data); + GPtrArray *modems; + GError *error = NULL; + + if (!dbus_g_proxy_end_call (proxy, call_id, &error, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &modems, + G_TYPE_INVALID)) { + nm_warning ("Could not get modem list: %s", error->message); + g_error_free (error); + } else { + int i; + + for (i = 0; i < modems->len; i++) { + char *path = (char *) g_ptr_array_index (modems, i); + + create_modem (manager, path); + g_free (path); + } + + g_ptr_array_free (modems, TRUE); + } +} + +static void +modem_manager_appeared (NMModemManager *self, gboolean enumerate_devices) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + if (priv->poke_id) { + g_source_remove (priv->poke_id); + priv->poke_id = 0; + } + + priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + MM_DBUS_SERVICE, MM_DBUS_PATH, MM_DBUS_INTERFACE); + + dbus_g_proxy_add_signal (priv->proxy, "DeviceAdded", G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DeviceAdded", + G_CALLBACK (modem_added), self, + NULL); + + dbus_g_proxy_add_signal (priv->proxy, "DeviceRemoved", G_TYPE_STRING, G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "DeviceRemoved", + G_CALLBACK (modem_removed), self, + NULL); + + if (enumerate_devices) + dbus_g_proxy_begin_call (priv->proxy, "EnumerateDevices", enumerate_devices_done, self, NULL, G_TYPE_INVALID); +} + +static gboolean +remove_one_modem (gpointer key, gpointer value, gpointer user_data) +{ + g_signal_emit (user_data, signals[DEVICE_REMOVED], 0, value); + + return TRUE; +} + +static void +modem_manager_disappeared (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + g_hash_table_foreach_remove (priv->modems, remove_one_modem, self); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + /* Try to activate the modem-manager */ + poke_modem_cb (self); + priv->poke_id = g_timeout_add (MODEM_POKE_INTERVAL, poke_modem_cb, self); +} + +static void +nm_modem_manager_name_owner_changed (NMDBusManager *dbus_mgr, + const char *name, + const char *old_owner, + const char *new_owner, + gpointer user_data) +{ + gboolean old_owner_good; + gboolean new_owner_good; + + /* Can't handle the signal if its not from the modem service */ + if (strcmp (MM_DBUS_SERVICE, name) != 0) + return; + + old_owner_good = (old_owner && strlen (old_owner)); + new_owner_good = (new_owner && strlen (new_owner)); + + if (!old_owner_good && new_owner_good) { + nm_info ("modem manager appeared"); + modem_manager_appeared (NM_MODEM_MANAGER (user_data), FALSE); + } else if (old_owner_good && !new_owner_good) { + nm_info ("modem manager disappeared"); + modem_manager_disappeared (NM_MODEM_MANAGER (user_data)); + } +} + +/*******************************************************/ + +static void +nm_modem_manager_init (NMModemManager *self) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (self); + + priv->modems = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->dbus_mgr = nm_dbus_manager_get (); + + g_signal_connect (priv->dbus_mgr, "name-owner-changed", + G_CALLBACK (nm_modem_manager_name_owner_changed), + self); + + if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, MM_DBUS_SERVICE)) + modem_manager_appeared (self, TRUE); + else + modem_manager_disappeared (self); +} + +static void +dispose (GObject *object) +{ + NMModemManagerPrivate *priv = NM_MODEM_MANAGER_GET_PRIVATE (object); + + if (priv->disposed) + return; + + priv->disposed = TRUE; + + if (priv->poke_id) { + g_source_remove (priv->poke_id); + priv->poke_id = 0; + } + + g_hash_table_foreach_remove (priv->modems, remove_one_modem, object); + g_hash_table_destroy (priv->modems); + + if (priv->proxy) { + g_object_unref (priv->proxy); + priv->proxy = NULL; + } + + if (priv->dbus_mgr) { + g_object_unref (priv->dbus_mgr); + priv->dbus_mgr = NULL; + } + + /* Chain up to the parent class */ + G_OBJECT_CLASS (nm_modem_manager_parent_class)->dispose (object); +} + +static void +nm_modem_manager_class_init (NMModemManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemManagerPrivate)); + + object_class->dispose = dispose; + + /* signals */ + signals[DEVICE_ADDED] = + g_signal_new ("device-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemManagerClass, device_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemManagerClass, device_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); +} diff --git a/src/modem-manager/nm-modem-manager.h b/src/modem-manager/nm-modem-manager.h new file mode 100644 index 0000000000..fb51f73988 --- /dev/null +++ b/src/modem-manager/nm-modem-manager.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#ifndef NM_MODEM_MANAGER_H +#define NM_MODEM_MANAGER_H + +#include <glib-object.h> +#include "nm-device.h" + +#define NM_TYPE_MODEM_MANAGER (nm_modem_manager_get_type ()) +#define NM_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM_MANAGER, NMModemManager)) +#define NM_MODEM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) +#define NM_IS_MODEM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM_MANAGER)) +#define NM_IS_MODEM_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM_MANAGER)) +#define NM_MODEM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM_MANAGER, NMModemManagerClass)) + +typedef struct { + GObject parent; +} NMModemManager; + +typedef struct { + GObjectClass parent; + + /* Signals */ + void (*device_added) (NMModemManager *manager, + NMDevice *device); + + void (*device_removed) (NMModemManager *manager, + NMDevice *device); +} NMModemManagerClass; + +GType nm_modem_manager_get_type (void); + +NMModemManager *nm_modem_manager_get (void); + +#endif /* NM_MODEM_MANAGER_H */ diff --git a/src/modem-manager/nm-modem-types.h b/src/modem-manager/nm-modem-types.h new file mode 100644 index 0000000000..4a5dad42ec --- /dev/null +++ b/src/modem-manager/nm-modem-types.h @@ -0,0 +1,90 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#ifndef NM_MODEM_TYPES_H +#define NM_MODEM_TYPES_H + +#define MM_DBUS_SERVICE "org.freedesktop.ModemManager" +#define MM_DBUS_PATH "/org/freedesktop/ModemManager" +#define MM_DBUS_INTERFACE "org.freedesktop.ModemManager" +#define MM_DBUS_INTERFACE_MODEM "org.freedesktop.ModemManager.Modem" +#define MM_DBUS_INTERFACE_MODEM_CDMA "org.freedesktop.ModemManager.Modem.Cdma" + +#define MM_DBUS_INTERFACE_MODEM_GSM_CARD "org.freedesktop.ModemManager.Modem.Gsm.Card" +#define MM_DBUS_INTERFACE_MODEM_GSM_NETWORK "org.freedesktop.ModemManager.Modem.Gsm.Network" +#define MM_DBUS_INTERFACE_MODEM_GSM_HSO "org.freedesktop.ModemManager.Modem.Gsm.Hso" +#define MM_DBUS_INTERFACE_MODEM_GSM_MBM "org.freedesktop.ModemManager.Modem.Gsm.Mbm" + +#define MM_MODEM_TYPE_UNKNOWN 0 +#define MM_MODEM_TYPE_GSM 1 +#define MM_MODEM_TYPE_CDMA 2 + +/* Errors */ + +#define MM_SERIAL_OPEN_FAILED MM_DBUS_INTERFACE_MODEM ".SerialOpenFailed" +#define MM_SERIAL_SEND_FAILED MM_DBUS_INTERFACE_MODEM ".SerialSendFailed" +#define MM_SERIAL_RESPONSE_TIMEOUT MM_DBUS_INTERFACE_MODEM ".SerialResponseTimeout" + +#define MM_MODEM_ERROR_GENERAL MM_DBUS_INTERFACE_MODEM ".General" +#define MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED MM_DBUS_INTERFACE_MODEM ".OperationNotSupported" + +#define MM_MODEM_CONNECT_ERROR_NO_CARRIER MM_DBUS_INTERFACE_MODEM ".NoCarrier" +#define MM_MODEM_CONNECT_ERROR_NO_DIALTONE MM_DBUS_INTERFACE_MODEM ".NoDialtone" +#define MM_MODEM_CONNECT_ERROR_BUSY MM_DBUS_INTERFACE_MODEM ".Busy" +#define MM_MODEM_CONNECT_ERROR_NO_ANSWER MM_DBUS_INTERFACE_MODEM ".NoAnswer" + +#define MM_MODEM_ERROR "org.freedesktop.ModemManager.Modem.Gsm" + +#define MM_MODEM_ERROR_PHONE_FAILURE MM_MODEM_ERROR ".PhoneFailure" +#define MM_MODEM_ERROR_NO_CONNECTION MM_MODEM_ERROR ".NoConnection" +#define MM_MODEM_ERROR_LINK_RESERVED MM_MODEM_ERROR ".LinkReserved" +#define MM_MODEM_ERROR_NOT_ALLOWED MM_MODEM_ERROR ".OperationNotAllowed" +#define MM_MODEM_ERROR_NOT_SUPPORTED MM_MODEM_ERROR ".OperationNotSupported" +#define MM_MODEM_ERROR_PH_SIM_PIN MM_MODEM_ERROR ".PhSimPinRequired" +#define MM_MODEM_ERROR_PH_FSIM_PIN MM_MODEM_ERROR ".PhFSimPinRequired" +#define MM_MODEM_ERROR_PH_FSIM_PUK MM_MODEM_ERROR ".PhFPukRequired" +#define MM_MODEM_ERROR_SIM_NOT_INSERTED MM_MODEM_ERROR ".SimNotInserted" +#define MM_MODEM_ERROR_SIM_PIN MM_MODEM_ERROR ".SimPinRequired" +#define MM_MODEM_ERROR_SIM_PUK MM_MODEM_ERROR ".SimPukRequired" +#define MM_MODEM_ERROR_SIM_FAILURE MM_MODEM_ERROR ".SimFailure" +#define MM_MODEM_ERROR_SIM_BUSY MM_MODEM_ERROR ".SimBusy" +#define MM_MODEM_ERROR_SIM_WRONG MM_MODEM_ERROR ".SimWrong" +#define MM_MODEM_ERROR_WRONG_PASSWORD MM_MODEM_ERROR ".IncorrectPassword" +#define MM_MODEM_ERROR_SIM_PIN2 MM_MODEM_ERROR ".SimPin2Required" +#define MM_MODEM_ERROR_SIM_PUK2 MM_MODEM_ERROR ".SimPuk2Required" +#define MM_MODEM_ERROR_MEMORY_FULL MM_MODEM_ERROR ".MemoryFull" +#define MM_MODEM_ERROR_INVALID_INDEX MM_MODEM_ERROR ".InvalidIndex" +#define MM_MODEM_ERROR_NOT_FOUND MM_MODEM_ERROR ".NotFound" +#define MM_MODEM_ERROR_MEMORY_FAILURE MM_MODEM_ERROR ".MemoryFailure" +#define MM_MODEM_ERROR_TEXT_TOO_LONG MM_MODEM_ERROR ".TextTooLong" +#define MM_MODEM_ERROR_INVALID_CHARS MM_MODEM_ERROR ".InvalidChars" +#define MM_MODEM_ERROR_DIAL_STRING_TOO_LONG MM_MODEM_ERROR ".DialStringTooLong" +#define MM_MODEM_ERROR_DIAL_STRING_INVALID MM_MODEM_ERROR ".InvalidDialString" +#define MM_MODEM_ERROR_NO_NETWORK MM_MODEM_ERROR ".NoNetwork" +#define MM_MODEM_ERROR_NETWORK_TIMEOUT MM_MODEM_ERROR ".NetworkTimeout" +#define MM_MODEM_ERROR_NETWORK_NOT_ALLOWED MM_MODEM_ERROR ".NetworkNotAllowed" +#define MM_MODEM_ERROR_NETWORK_PIN MM_MODEM_ERROR ".NetworkPinRequired" +#define MM_MODEM_ERROR_NETWORK_PUK MM_MODEM_ERROR ".NetworkPukRequired" +#define MM_MODEM_ERROR_NETWORK_SUBSET_PIN MM_MODEM_ERROR ".NetworkSubsetPinRequired" +#define MM_MODEM_ERROR_NETWORK_SUBSET_PUK MM_MODEM_ERROR ".NetworkSubsetPukRequired" +#define MM_MODEM_ERROR_SERVICE_PIN MM_MODEM_ERROR ".ServicePinRequired" +#define MM_MODEM_ERROR_SERVICE_PUK MM_MODEM_ERROR ".ServicePukRequired" +#define MM_MODEM_ERROR_CORP_PIN MM_MODEM_ERROR ".CorporatePinRequired" +#define MM_MODEM_ERROR_CORP_PUK MM_MODEM_ERROR ".CorporatePukRequired" +#define MM_MODEM_ERROR_HIDDEN_KEY MM_MODEM_ERROR ".HiddenKeyRequired" +#define MM_MODEM_ERROR_EAP_NOT_SUPPORTED MM_MODEM_ERROR ".EapMethodNotSupported" +#define MM_MODEM_ERROR_INCORRECT_PARAMS MM_MODEM_ERROR ".IncorrectParams" +#define MM_MODEM_ERROR_UNKNOWN MM_MODEM_ERROR ".Unknown" +#define MM_MODEM_ERROR_GPRS_ILLEGAL_MS MM_MODEM_ERROR ".GprsIllegalMs" +#define MM_MODEM_ERROR_GPRS_ILLEGAL_ME MM_MODEM_ERROR ".GprsIllegalMe" +#define MM_MODEM_ERROR_GPRS_SERVICE_NOT_ALLOWED MM_MODEM_ERROR ".GprsServiceNotAllowed" +#define MM_MODEM_ERROR_GPRS_PLMN_NOT_ALLOWED MM_MODEM_ERROR ".GprsPlmnNotAllowed" +#define MM_MODEM_ERROR_GPRS_LOCATION_NOT_ALLOWED MM_MODEM_ERROR ".GprsLocationNotAllowed" +#define MM_MODEM_ERROR_GPRS_ROAMING_NOT_ALLOWED MM_MODEM_ERROR ".GprsRoamingNotAllowed" +#define MM_MODEM_ERROR_GPRS_OPTION_NOT_SUPPORTED MM_MODEM_ERROR ".GprsOptionNotSupported" +#define MM_MODEM_ERROR_GPRS_NOT_SUBSCRIBED MM_MODEM_ERROR ".GprsNotSubscribed" +#define MM_MODEM_ERROR_GPRS_OUT_OF_ORDER MM_MODEM_ERROR ".GprsOutOfOrder" +#define MM_MODEM_ERROR_GPRS_PDP_AUTH_FAILURE MM_MODEM_ERROR ".GprsPdpAuthFailure" +#define MM_MODEM_ERROR_GPRS_UNKNOWN MM_MODEM_ERROR ".GprsUnspecified" +#define MM_MODEM_ERROR_GPRS_INVALID_CLASS MM_MODEM_ERROR ".GprsInvalidClass" + +#endif /* NM_MODEM_TYPES_H */ diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c new file mode 100644 index 0000000000..fddfd059fd --- /dev/null +++ b/src/modem-manager/nm-modem.c @@ -0,0 +1,457 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#include <string.h> +#include "nm-modem.h" +#include "nm-device-private.h" +#include "nm-device-interface.h" +#include "nm-dbus-manager.h" +#include "nm-setting-connection.h" +#include "nm-setting-gsm.h" +#include "nm-setting-cdma.h" +#include "nm-marshal.h" +#include "nm-properties-changed-signal.h" +#include "nm-modem-types.h" +#include "nm-utils.h" +#include "nm-serial-device-glue.h" + +G_DEFINE_TYPE (NMModem, nm_modem, NM_TYPE_DEVICE) + +#define NM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM, NMModemPrivate)) + +enum { + PROP_0, + PROP_PATH, + + LAST_PROP +}; + +typedef struct { + NMDBusManager *dbus_mgr; + char *path; + DBusGProxy *proxy; + NMPPPManager *ppp_manager; + NMIP4Config *pending_ip4_config; + + guint state_to_disconnected_id; + + /* PPP stats */ + guint32 in_bytes; + guint32 out_bytes; +} NMModemPrivate; + +enum { + PPP_STATS, + PROPERTIES_CHANGED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +NMPPPManager * +nm_modem_get_ppp_manager (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + return NM_MODEM_GET_PRIVATE (self)->ppp_manager; +} + +DBusGProxy * +nm_modem_get_proxy (NMModem *self, + const char *interface) +{ + + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + const char *current_iface; + + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + /* Default to the default interface. */ + if (interface == NULL) + interface = MM_DBUS_INTERFACE_MODEM; + + current_iface = dbus_g_proxy_get_interface (priv->proxy); + if (!current_iface || strcmp (current_iface, interface)) + dbus_g_proxy_set_interface (priv->proxy, interface); + + return priv->proxy; +} + +void +nm_modem_connect (NMModem *self, + const char *number) +{ + g_return_if_fail (NM_IS_MODEM (self)); + + NM_MODEM_GET_CLASS (self)->connect (self, number); +} + +const char * +nm_modem_get_ppp_name (NMModem *self, + NMConnection *connection) +{ + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL); + + if (NM_MODEM_GET_CLASS (self)->get_ppp_name) + return NM_MODEM_GET_CLASS (self)->get_ppp_name (self, connection); + + return NULL; +} + +static void +ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + switch (status) { + case NM_PPP_STATUS_NETWORK: + nm_device_state_changed (device, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); + break; + case NM_PPP_STATUS_DISCONNECT: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT); + break; + case NM_PPP_STATUS_DEAD: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED); + break; + case NM_PPP_STATUS_AUTHENTICATE: + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + break; + default: + break; + } +} + +static void +ppp_ip4_config (NMPPPManager *ppp_manager, + const char *iface, + NMIP4Config *config, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + + nm_device_set_ip_iface (device, iface); + NM_MODEM_GET_PRIVATE (device)->pending_ip4_config = g_object_ref (config); + nm_device_activate_schedule_stage4_ip_config_get (device); +} + +static void +ppp_stats (NMPPPManager *ppp_manager, + guint32 in_bytes, + guint32 out_bytes, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) { + priv->in_bytes = in_bytes; + priv->out_bytes = out_bytes; + + g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes); + } +} + +static NMActStageReturn +real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (device); + NMActRequest *req; + const char *ppp_name = NULL; + GError *err = NULL; + NMActStageReturn ret; + + req = nm_device_get_act_request (device); + g_assert (req); + + ppp_name = nm_modem_get_ppp_name (NM_MODEM (device), + nm_act_request_get_connection (req)); + + priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (device)); + if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, &err)) { + g_signal_connect (priv->ppp_manager, "state-changed", + G_CALLBACK (ppp_state_changed), + device); + g_signal_connect (priv->ppp_manager, "ip4-config", + G_CALLBACK (ppp_ip4_config), + device); + g_signal_connect (priv->ppp_manager, "stats", + G_CALLBACK (ppp_stats), + device); + + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else { + nm_warning ("%s", err->message); + g_error_free (err); + + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + + *reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED; + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + + return ret; +} + +static NMActStageReturn +real_act_stage4_get_ip4_config (NMDevice *device, + NMIP4Config **config, + NMDeviceStateReason *reason) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (device); + + *config = priv->pending_ip4_config; + priv->pending_ip4_config = NULL; + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +real_deactivate_quickly (NMDevice *device) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (device); + + nm_device_set_ip_iface (device, NULL); + + if (priv->pending_ip4_config) { + g_object_unref (priv->pending_ip4_config); + priv->pending_ip4_config = NULL; + } + + priv->in_bytes = priv->out_bytes = 0; + + if (priv->ppp_manager) { + g_object_unref (priv->ppp_manager); + priv->ppp_manager = NULL; + } + + dbus_g_proxy_call_no_reply (nm_modem_get_proxy (NM_MODEM (device), NULL), + "Enable", G_TYPE_BOOLEAN, FALSE, G_TYPE_INVALID); +} + +static guint32 +real_get_generic_capabilities (NMDevice *dev) +{ + return NM_DEVICE_CAP_NM_SUPPORTED; +} + + +static void +connect_done (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + GError *error = NULL; + + if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) + nm_device_activate_schedule_stage2_device_config (device); + else { + nm_warning ("Connect failed: %s", error->message); + g_error_free (error); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_DIAL_FAILED); + } +} + +static void +real_connect (NMModem *modem, const char *number) +{ + dbus_g_proxy_begin_call_with_timeout (nm_modem_get_proxy (modem, MM_DBUS_INTERFACE_MODEM), + "Connect", connect_done, + modem, NULL, 60000, + G_TYPE_STRING, number ? number : "", + G_TYPE_INVALID); +} + +static gboolean +unavailable_to_disconnected (gpointer user_data) +{ + nm_device_state_changed (NM_DEVICE (user_data), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + return FALSE; +} + +static void +device_state_changed (NMDeviceInterface *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data) +{ + NMModem *self = NM_MODEM (user_data); + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + /* Remove any previous delayed transition to disconnected */ + if (priv->state_to_disconnected_id) { + g_source_remove (priv->state_to_disconnected_id); + priv->state_to_disconnected_id = 0; + } + + /* If transitioning to UNAVAILBLE and we have a carrier, transition to + * DISCONNECTED because the device is ready to use. Otherwise the carrier-on + * handler will handle the transition to DISCONNECTED when the carrier is detected. + */ + if (new_state == NM_DEVICE_STATE_UNAVAILABLE) + priv->state_to_disconnected_id = g_idle_add (unavailable_to_disconnected, user_data); + + /* Make sure we don't leave the serial device open */ + switch (new_state) { + case NM_DEVICE_STATE_NEED_AUTH: + if (priv->ppp_manager) + break; + /* else fall through */ + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_FAILED: + case NM_DEVICE_STATE_DISCONNECTED: + dbus_g_proxy_call_no_reply (nm_modem_get_proxy (self, NULL), + "Disconnect", G_TYPE_INVALID); + break; + default: + break; + } +} + +/*****************************************************************************/ + +static void +nm_modem_init (NMModem *self) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + + priv->dbus_mgr = nm_dbus_manager_get (); +} + +static GObject* +constructor (GType type, + guint n_construct_params, + GObjectConstructParam *construct_params) +{ + GObject *object; + NMModemPrivate *priv; + + object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type, + n_construct_params, + construct_params); + if (!object) + return NULL; + + priv = NM_MODEM_GET_PRIVATE (object); + + if (!priv->path) { + g_warning ("DBus path not provided"); + goto err; + } + + priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr), + MM_DBUS_SERVICE, priv->path, MM_DBUS_INTERFACE_MODEM); + + g_signal_connect (object, "state-changed", G_CALLBACK (device_state_changed), object); + + return object; + + err: + g_object_unref (object); + return NULL; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + 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) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PATH: + /* Construct only */ + priv->path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +finalize (GObject *object) +{ + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + + if (priv->state_to_disconnected_id) { + g_source_remove (priv->state_to_disconnected_id); + priv->state_to_disconnected_id = 0; + } + + if (priv->proxy) + g_object_unref (priv->proxy); + + g_object_unref (priv->dbus_mgr); + + G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object); +} + +static void +nm_modem_class_init (NMModemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMModemPrivate)); + + /* Virtual methods */ + object_class->constructor = constructor; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->finalize = finalize; + + device_class->get_generic_capabilities = real_get_generic_capabilities; + device_class->act_stage2_config = real_act_stage2_config; + device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; + device_class->deactivate_quickly = real_deactivate_quickly; + + klass->connect = real_connect; + + /* Properties */ + g_object_class_install_property + (object_class, PROP_PATH, + g_param_spec_string (NM_MODEM_PATH, + "DBus path", + "DBus path", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + /* Signals */ + signals[PPP_STATS] = + g_signal_new ("ppp-stats", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMModemClass, ppp_stats), + NULL, NULL, + _nm_marshal_VOID__UINT_UINT, + G_TYPE_NONE, 2, + G_TYPE_UINT, G_TYPE_UINT); + + signals[PROPERTIES_CHANGED] = + nm_properties_changed_signal_new (object_class, + G_STRUCT_OFFSET (NMModemClass, properties_changed)); + + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), + &dbus_glib_nm_serial_device_object_info); +} diff --git a/src/modem-manager/nm-modem.h b/src/modem-manager/nm-modem.h new file mode 100644 index 0000000000..36b9bd8202 --- /dev/null +++ b/src/modem-manager/nm-modem.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +#ifndef NM_MODEM_H +#define NM_MODEM_H + +#include <dbus/dbus-glib.h> +#include <nm-device.h> +#include "ppp-manager/nm-ppp-manager.h" + +G_BEGIN_DECLS + +#define NM_TYPE_MODEM (nm_modem_get_type ()) +#define NM_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MODEM, NMModem)) +#define NM_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_MODEM, NMModemClass)) +#define NM_IS_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_MODEM)) +#define NM_IS_MODEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_MODEM)) +#define NM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM, NMModemClass)) + +#define NM_MODEM_PATH "path" + +typedef struct { + NMDevice parent; +} NMModem; + +typedef struct { + NMDeviceClass parent; + + void (*connect) (NMModem *self, + const char *number); + + const char *(*get_ppp_name) (NMModem *self, + NMConnection *connection); + + /* Signals */ + void (*ppp_stats) (NMModem *self, guint32 in_bytes, guint32 out_bytes); + void (*properties_changed) (NMModem *self, GHashTable *properties); +} NMModemClass; + +GType nm_modem_get_type (void); + +/* Protected */ + +NMPPPManager *nm_modem_get_ppp_manager (NMModem *self); +DBusGProxy *nm_modem_get_proxy (NMModem *self, + const char *interface); + +void nm_modem_connect (NMModem *self, + const char *number); + +const char *nm_modem_get_ppp_name (NMModem *self, + NMConnection *connection); + +G_END_DECLS + +#endif /* NM_MODEM_H */ |