/* -*- 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) 2009 - 2010 Red Hat, Inc. */ #include #include #include #include "nm-glib-compat.h" #include "nm-bluez-common.h" #include "nm-dbus-manager.h" #include "nm-device-bt.h" #include "nm-device-interface.h" #include "nm-device-private.h" #include "nm-logging.h" #include "nm-marshal.h" #include "ppp-manager/nm-ppp-manager.h" #include "nm-properties-changed-signal.h" #include "nm-setting-connection.h" #include "nm-setting-bluetooth.h" #include "nm-setting-cdma.h" #include "nm-setting-gsm.h" #include "nm-device-bt-glue.h" #include "NetworkManagerUtils.h" #define BLUETOOTH_DUN_UUID "dun" #define BLUETOOTH_NAP_UUID "nap" G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE) #define NM_DEVICE_BT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_BT, NMDeviceBtPrivate)) typedef struct { char *bdaddr; char *name; guint32 capabilities; gboolean connected; gboolean have_iface; DBusGProxy *type_proxy; DBusGProxy *dev_proxy; char *rfcomm_iface; NMModem *modem; guint32 timeout_id; guint32 bt_type; /* BT type of the current connection */ } NMDeviceBtPrivate; enum { PROP_0, PROP_HW_ADDRESS, PROP_BT_NAME, PROP_BT_CAPABILITIES, LAST_PROP }; enum { PPP_STATS, PROPERTIES_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; typedef enum { NM_BT_ERROR_CONNECTION_NOT_BT = 0, NM_BT_ERROR_CONNECTION_INVALID, NM_BT_ERROR_CONNECTION_INCOMPATIBLE, } NMBtError; #define NM_BT_ERROR (nm_bt_error_quark ()) #define NM_TYPE_BT_ERROR (nm_bt_error_get_type ()) static GQuark nm_bt_error_quark (void) { static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("nm-bt-error"); return quark; } /* This should really be standard. */ #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } static GType nm_bt_error_get_type (void) { static GType etype = 0; if (etype == 0) { static const GEnumValue values[] = { /* Connection was not a BT connection. */ ENUM_ENTRY (NM_BT_ERROR_CONNECTION_NOT_BT, "ConnectionNotBt"), /* Connection was not a valid BT connection. */ ENUM_ENTRY (NM_BT_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), /* Connection does not apply to this device. */ ENUM_ENTRY (NM_BT_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), { 0, 0, 0 } }; etype = g_enum_register_static ("NMBtError", values); } return etype; } guint32 nm_device_bt_get_capabilities (NMDeviceBt *self) { g_return_val_if_fail (self != NULL, NM_BT_CAPABILITY_NONE); g_return_val_if_fail (NM_IS_DEVICE_BT (self), NM_BT_CAPABILITY_NONE); return NM_DEVICE_BT_GET_PRIVATE (self)->capabilities; } const char *nm_device_bt_get_hw_address (NMDeviceBt *self) { g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_DEVICE_BT (self), NULL); return NM_DEVICE_BT_GET_PRIVATE (self)->bdaddr; } static guint32 get_connection_bt_type (NMConnection *connection) { NMSettingBluetooth *s_bt; const char *bt_type; s_bt = (NMSettingBluetooth *) nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH); if (!s_bt) return NM_BT_CAPABILITY_NONE; bt_type = nm_setting_bluetooth_get_connection_type (s_bt); g_assert (bt_type); if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_DUN)) return NM_BT_CAPABILITY_DUN; else if (!strcmp (bt_type, NM_SETTING_BLUETOOTH_TYPE_PANU)) return NM_BT_CAPABILITY_NAP; return NM_BT_CAPABILITY_NONE; } static NMConnection * real_get_best_auto_connection (NMDevice *device, GSList *connections, char **specific_object) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); GSList *iter; for (iter = connections; iter; iter = g_slist_next (iter)) { NMConnection *connection = NM_CONNECTION (iter->data); NMSettingConnection *s_con; guint32 bt_type; 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_BLUETOOTH_SETTING_NAME)) continue; bt_type = get_connection_bt_type (connection); if (!(bt_type & priv->capabilities)) continue; return connection; } return NULL; } static gboolean real_check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); NMSettingConnection *s_con; NMSettingBluetooth *s_bt; const GByteArray *array; char *str; int addr_match = FALSE; guint32 bt_type; s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); g_assert (s_con); if (strcmp (nm_setting_connection_get_connection_type (s_con), NM_SETTING_BLUETOOTH_SETTING_NAME)) { g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_CONNECTION_NOT_BT, "The connection was not a Bluetooth connection."); return FALSE; } s_bt = NM_SETTING_BLUETOOTH (nm_connection_get_setting (connection, NM_TYPE_SETTING_BLUETOOTH)); if (!s_bt) { g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INVALID, "The connection was not a valid Bluetooth connection."); return FALSE; } array = nm_setting_bluetooth_get_bdaddr (s_bt); if (!array || (array->len != ETH_ALEN)) { g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INVALID, "The connection did not contain a valid Bluetooth address."); return FALSE; } bt_type = get_connection_bt_type (connection); if (!(bt_type & priv->capabilities)) { g_set_error (error, NM_BT_ERROR, NM_BT_ERROR_CONNECTION_INCOMPATIBLE, "The connection was not compatible with the device's capabilities."); return FALSE; } str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", array->data[0], array->data[1], array->data[2], array->data[3], array->data[4], array->data[5]); addr_match = !strcmp (priv->bdaddr, str); g_free (str); return addr_match; } static guint32 real_get_generic_capabilities (NMDevice *dev) { return NM_DEVICE_CAP_NM_SUPPORTED; } /*****************************************************************************/ /* IP method PPP */ static void ppp_stats (NMModem *modem, guint32 in_bytes, guint32 out_bytes, gpointer user_data) { g_signal_emit (NM_DEVICE_BT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes); } static void ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data) { NMDevice *device = NM_DEVICE (user_data); switch (nm_device_interface_get_state (NM_DEVICE_INTERFACE (device))) { case NM_DEVICE_STATE_PREPARE: case NM_DEVICE_STATE_CONFIG: case NM_DEVICE_STATE_NEED_AUTH: case NM_DEVICE_STATE_IP_CONFIG: case NM_DEVICE_STATE_ACTIVATED: nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); break; default: break; } } static void modem_need_auth (NMModem *modem, const char *setting_name, gboolean retry, RequestSecretsCaller caller, const char *hint1, const char *hint2, gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); NMActRequest *req; req = nm_device_get_act_request (NM_DEVICE (self)); g_assert (req); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); nm_act_request_get_secrets (req, setting_name, retry, caller, hint1, hint2); } static void modem_prepare_result (NMModem *modem, gboolean success, NMDeviceStateReason reason, gpointer user_data) { NMDevice *device = NM_DEVICE (user_data); NMDeviceState state; state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); g_return_if_fail (state == NM_DEVICE_STATE_CONFIG || state == NM_DEVICE_STATE_NEED_AUTH); if (success) { NMActRequest *req; NMActStageReturn ret; NMDeviceStateReason stage2_reason = NM_DEVICE_STATE_REASON_NONE; req = nm_device_get_act_request (device); g_assert (req); ret = nm_modem_act_stage2_config (modem, req, &stage2_reason); switch (ret) { case NM_ACT_STAGE_RETURN_POSTPONE: break; case NM_ACT_STAGE_RETURN_SUCCESS: nm_device_activate_schedule_stage3_ip_config_start (device); break; case NM_ACT_STAGE_RETURN_FAILURE: default: nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, stage2_reason); break; } } else nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); } static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, NMDeviceStateReason reason, gpointer user_data) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); if (priv->modem) nm_modem_device_state_changed (priv->modem, new_state, old_state, reason); } static void modem_ip4_config_result (NMModem *self, const char *iface, NMIP4Config *config, GError *error, gpointer user_data) { NMDevice *device = NM_DEVICE (user_data); NMDeviceState state; state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (device)); g_return_if_fail (state == NM_DEVICE_STATE_IP_CONFIG); if (error) { nm_log_warn (LOGD_MB | LOGD_IP4 | LOGD_BT, "(%s): retrieving IP4 configuration failed: (%d) %s", nm_device_get_ip_iface (device), error ? error->code : -1, error && error->message ? error->message : "(unknown)"); nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); } else { if (iface) nm_device_set_ip_iface (device, iface); nm_device_activate_schedule_stage4_ip4_config_get (device); } } static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason) { NMActRequest *req; NMActStageReturn ret; g_return_val_if_fail (reason != NULL, FALSE); req = nm_device_get_act_request (NM_DEVICE (self)); g_assert (req); ret = nm_modem_act_stage1_prepare (modem, req, reason); switch (ret) { case NM_ACT_STAGE_RETURN_POSTPONE: case NM_ACT_STAGE_RETURN_SUCCESS: /* Success, wait for the 'prepare-result' signal */ return TRUE; case NM_ACT_STAGE_RETURN_FAILURE: default: break; } return FALSE; } static void real_connection_secrets_updated (NMDevice *device, NMConnection *connection, GSList *updated_settings, RequestSecretsCaller caller) { NMDeviceBt *self = NM_DEVICE_BT (device); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); NMActRequest *req; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; g_return_if_fail (IS_ACTIVATING_STATE (nm_device_get_state (device))); req = nm_device_get_act_request (device); g_assert (req); if (!nm_modem_connection_secrets_updated (priv->modem, req, connection, updated_settings, caller)) { nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); return; } /* PPP handles stuff itself... */ if (caller == SECRETS_CALLER_PPP) return; /* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */ g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); if (!modem_stage1 (self, priv->modem, &reason)) nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason); } /*****************************************************************************/ gboolean nm_device_bt_modem_added (NMDeviceBt *self, NMModem *modem, const char *driver) { NMDeviceBtPrivate *priv; const char *modem_iface; char *base; NMDeviceState state; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DEVICE_BT (self), FALSE); g_return_val_if_fail (modem != NULL, FALSE); g_return_val_if_fail (NM_IS_MODEM (modem), FALSE); priv = NM_DEVICE_BT_GET_PRIVATE (self); modem_iface = nm_modem_get_iface (modem); g_return_val_if_fail (modem_iface != NULL, FALSE); if (!priv->rfcomm_iface) return FALSE; base = g_path_get_basename (priv->rfcomm_iface); if (strcmp (base, modem_iface)) { g_free (base); return FALSE; } g_free (base); /* Got the modem */ if (priv->timeout_id) { g_source_remove (priv->timeout_id); priv->timeout_id = 0; } /* Can only accept the modem in stage2, but since the interface matched * what we were expecting, don't let anything else claim the modem either. */ state = nm_device_interface_get_state (NM_DEVICE_INTERFACE (self)); if (state != NM_DEVICE_STATE_CONFIG) { nm_log_warn (LOGD_BT | LOGD_MB, "(%s): modem found but device not in correct state (%d)", nm_device_get_iface (NM_DEVICE (self)), nm_device_get_state (NM_DEVICE (self))); return TRUE; } nm_log_info (LOGD_BT | LOGD_MB, "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) modem found.", nm_device_get_iface (NM_DEVICE (self))); if (priv->modem) { g_warn_if_reached (); g_object_unref (priv->modem); } priv->modem = g_object_ref (modem); g_signal_connect (modem, NM_MODEM_PPP_STATS, G_CALLBACK (ppp_stats), self); g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self); g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self); g_signal_connect (modem, NM_MODEM_IP4_CONFIG_RESULT, G_CALLBACK (modem_ip4_config_result), self); g_signal_connect (modem, NM_MODEM_NEED_AUTH, G_CALLBACK (modem_need_auth), self); /* Kick off the modem connection */ if (!modem_stage1 (self, modem, &reason)) nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason); return TRUE; } gboolean nm_device_bt_modem_removed (NMDeviceBt *self, NMModem *modem) { NMDeviceBtPrivate *priv; NMDeviceState state; g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (NM_IS_DEVICE_BT (self), FALSE); g_return_val_if_fail (modem != NULL, FALSE); g_return_val_if_fail (NM_IS_MODEM (modem), FALSE); priv = NM_DEVICE_BT_GET_PRIVATE (self); if (modem != priv->modem) return FALSE; state = nm_device_get_state (NM_DEVICE (self)); nm_modem_device_state_changed (priv->modem, NM_DEVICE_STATE_DISCONNECTED, state, NM_DEVICE_STATE_REASON_USER_REQUESTED); g_object_unref (priv->modem); priv->modem = NULL; return TRUE; } static gboolean modem_find_timeout (gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND); return FALSE; } static void check_connect_continue (NMDeviceBt *self) { NMDevice *device = NM_DEVICE (self); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); gboolean pan = (priv->bt_type == NM_BT_CAPABILITY_NAP); gboolean dun = (priv->bt_type == NM_BT_CAPABILITY_DUN); if (!priv->connected || !priv->have_iface) return; nm_log_info (LOGD_BT, "Activation (%s %s/bluetooth) Stage 2 of 5 (Device Configure) " "successful. Will connect via %s.", nm_device_get_iface (device), nm_device_get_ip_iface (device), dun ? "DUN" : (pan ? "PAN" : "unknown")); if (pan) { /* Bluez says we're connected now. Start IP config. */ nm_device_activate_schedule_stage3_ip_config_start (device); } else if (dun) { /* Wait for ModemManager to find the modem */ if (priv->timeout_id) g_source_remove (priv->timeout_id); priv->timeout_id = g_timeout_add_seconds (20, modem_find_timeout, self); nm_log_info (LOGD_BT | LOGD_MB, "Activation (%s/bluetooth) Stage 2 of 5 (Device Configure) " "waiting for modem to appear.", nm_device_get_iface (device)); } else g_assert_not_reached (); } static void bluez_connect_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, void *user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); GError *error = NULL; char *device; if (dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_STRING, &device, G_TYPE_INVALID) == FALSE) { nm_log_warn (LOGD_BT, "Error connecting with bluez: %s", error && error->message ? error->message : "(unknown)"); g_clear_error (&error); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); return; } if (!device || !strlen (device)) { nm_log_warn (LOGD_BT, "Invalid network device returned by bluez"); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); } if (priv->bt_type == NM_BT_CAPABILITY_DUN) { g_free (priv->rfcomm_iface); priv->rfcomm_iface = device; } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { nm_device_set_ip_iface (NM_DEVICE (self), device); g_free (device); } /* Stage 3 gets scheduled when Bluez says we're connected */ priv->have_iface = TRUE; check_connect_continue (self); } static void bluez_property_changed (DBusGProxy *proxy, const char *property, GValue *value, gpointer user_data) { NMDevice *device = NM_DEVICE (user_data); NMDeviceBt *self = NM_DEVICE_BT (user_data); NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); gboolean connected; NMDeviceState state; if (strcmp (property, "Connected")) return; state = nm_device_get_state (device); connected = g_value_get_boolean (value); if (connected) { if (state == NM_DEVICE_STATE_CONFIG) { priv->connected = TRUE; check_connect_continue (self); } } else { gboolean fail = FALSE; /* Bluez says we're disconnected from the device. Suck. */ if (nm_device_is_activating (device)) { nm_log_info (LOGD_BT, "Activation (%s/bluetooth): bluetooth link disconnected.", nm_device_get_iface (device)); fail = TRUE; } else if (state == NM_DEVICE_STATE_ACTIVATED) { nm_log_info (LOGD_BT, "(%s): bluetooth link disconnected.", nm_device_get_iface (device)); fail = TRUE; } if (fail) { nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CARRIER); priv->connected = FALSE; } } } static gboolean bt_connect_timeout (gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); return FALSE; } static NMActStageReturn real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); NMActRequest *req; NMDBusManager *dbus_mgr; DBusGConnection *g_connection; gboolean dun = FALSE; req = nm_device_get_act_request (device); g_assert (req); priv->bt_type = get_connection_bt_type (nm_act_request_get_connection (req)); if (priv->bt_type == NM_BT_CAPABILITY_NONE) { // FIXME: set a reason code return NM_ACT_STAGE_RETURN_FAILURE; } dbus_mgr = nm_dbus_manager_get (); g_connection = nm_dbus_manager_get_connection (dbus_mgr); g_object_unref (dbus_mgr); if (priv->bt_type == NM_BT_CAPABILITY_DUN) dun = TRUE; else if (priv->bt_type == NM_BT_CAPABILITY_NAP) dun = FALSE; else g_assert_not_reached (); priv->dev_proxy = dbus_g_proxy_new_for_name (g_connection, BLUEZ_SERVICE, nm_device_get_udi (device), BLUEZ_DEVICE_INTERFACE); if (!priv->dev_proxy) { // FIXME: set a reason code return NM_ACT_STAGE_RETURN_FAILURE; } /* Watch for BT device property changes */ dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_add_signal (priv->dev_proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->dev_proxy, "PropertyChanged", G_CALLBACK (bluez_property_changed), device, NULL); priv->type_proxy = dbus_g_proxy_new_for_name (g_connection, BLUEZ_SERVICE, nm_device_get_udi (device), dun ? BLUEZ_SERIAL_INTERFACE : BLUEZ_NETWORK_INTERFACE); if (!priv->type_proxy) { // FIXME: set a reason code return NM_ACT_STAGE_RETURN_FAILURE; } /* Connect to the BT device */ dbus_g_proxy_begin_call_with_timeout (priv->type_proxy, "Connect", bluez_connect_cb, device, NULL, 20000, G_TYPE_STRING, dun ? BLUETOOTH_DUN_UUID : BLUETOOTH_NAP_UUID, G_TYPE_INVALID); if (priv->timeout_id) g_source_remove (priv->timeout_id); priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device); return NM_ACT_STAGE_RETURN_POSTPONE; } static NMActStageReturn real_act_stage3_ip4_config_start (NMDevice *device, NMDeviceStateReason *reason) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); NMActStageReturn ret; if (priv->bt_type == NM_BT_CAPABILITY_DUN) { ret = nm_modem_stage3_ip4_config_start (NM_DEVICE_BT_GET_PRIVATE (device)->modem, device, NM_DEVICE_CLASS (nm_device_bt_parent_class), reason); } else ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, reason); return ret; } static NMActStageReturn real_act_stage4_get_ip4_config (NMDevice *device, NMIP4Config **config, NMDeviceStateReason *reason) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); NMActStageReturn ret; if (priv->bt_type == NM_BT_CAPABILITY_DUN) { ret = nm_modem_stage4_get_ip4_config (NM_DEVICE_BT_GET_PRIVATE (device)->modem, device, NM_DEVICE_CLASS (nm_device_bt_parent_class), config, reason); } else ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage4_get_ip4_config (device, config, reason); return ret; } static void real_deactivate_quickly (NMDevice *device) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (device); priv->have_iface = FALSE; priv->connected = FALSE; if (priv->bt_type == NM_BT_CAPABILITY_DUN) { if (priv->modem) { nm_modem_deactivate_quickly (priv->modem, device); /* Since we're killing the Modem object before it'll get the * state change signal, simulate the state change here. */ nm_modem_device_state_changed (priv->modem, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_USER_REQUESTED); g_object_unref (priv->modem); priv->modem = NULL; } if (priv->type_proxy) { /* Don't ever pass NULL through dbus; rfcomm_iface * might happen to be NULL for some reason. */ if (priv->rfcomm_iface) { dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect", G_TYPE_STRING, priv->rfcomm_iface, G_TYPE_INVALID); } g_object_unref (priv->type_proxy); priv->type_proxy = NULL; } } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { if (priv->type_proxy) { dbus_g_proxy_call_no_reply (priv->type_proxy, "Disconnect", G_TYPE_INVALID); g_object_unref (priv->type_proxy); priv->type_proxy = NULL; } } if (priv->dev_proxy) { g_object_unref (priv->dev_proxy); priv->dev_proxy = NULL; } if (priv->timeout_id) { g_source_remove (priv->timeout_id); priv->timeout_id = 0; } priv->bt_type = NM_BT_CAPABILITY_NONE; g_free (priv->rfcomm_iface); priv->rfcomm_iface = NULL; if (NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate_quickly) NM_DEVICE_CLASS (nm_device_bt_parent_class)->deactivate_quickly (device); } /*****************************************************************************/ NMDevice * nm_device_bt_new (const char *udi, const char *bdaddr, const char *name, guint32 capabilities, gboolean managed) { NMDevice *device; g_return_val_if_fail (udi != NULL, NULL); g_return_val_if_fail (bdaddr != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (capabilities != NM_BT_CAPABILITY_NONE, NULL); device = (NMDevice *) g_object_new (NM_TYPE_DEVICE_BT, NM_DEVICE_INTERFACE_UDI, udi, NM_DEVICE_INTERFACE_IFACE, bdaddr, NM_DEVICE_INTERFACE_DRIVER, "bluez", NM_DEVICE_BT_HW_ADDRESS, bdaddr, NM_DEVICE_BT_NAME, name, NM_DEVICE_BT_CAPABILITIES, capabilities, NM_DEVICE_INTERFACE_MANAGED, managed, NM_DEVICE_INTERFACE_TYPE_DESC, "Bluetooth", NM_DEVICE_INTERFACE_DEVICE_TYPE, NM_DEVICE_TYPE_BT, NULL); if (device) g_signal_connect (device, "state-changed", G_CALLBACK (device_state_changed), device); return device; } static void nm_device_bt_init (NMDeviceBt *self) { } static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); switch (prop_id) { case PROP_HW_ADDRESS: /* Construct only */ priv->bdaddr = g_ascii_strup (g_value_get_string (value), -1); break; case PROP_BT_NAME: /* Construct only */ priv->name = g_value_dup_string (value); break; case PROP_BT_CAPABILITIES: /* Construct only */ priv->capabilities = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); switch (prop_id) { case PROP_HW_ADDRESS: g_value_set_string (value, priv->bdaddr); break; case PROP_BT_NAME: g_value_set_string (value, priv->name); break; case PROP_BT_CAPABILITIES: g_value_set_uint (value, priv->capabilities); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void finalize (GObject *object) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (object); if (priv->timeout_id) { g_source_remove (priv->timeout_id); priv->timeout_id = 0; } if (priv->type_proxy) g_object_unref (priv->type_proxy); if (priv->dev_proxy) g_object_unref (priv->dev_proxy); if (priv->modem) g_object_unref (priv->modem); g_free (priv->rfcomm_iface); g_free (priv->bdaddr); g_free (priv->name); G_OBJECT_CLASS (nm_device_bt_parent_class)->finalize (object); } static void nm_device_bt_class_init (NMDeviceBtClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); g_type_class_add_private (object_class, sizeof (NMDeviceBtPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->finalize = finalize; device_class->get_best_auto_connection = real_get_best_auto_connection; device_class->get_generic_capabilities = real_get_generic_capabilities; device_class->connection_secrets_updated = real_connection_secrets_updated; device_class->deactivate_quickly = real_deactivate_quickly; device_class->act_stage2_config = real_act_stage2_config; device_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start; device_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config; device_class->check_connection_compatible = real_check_connection_compatible; /* Properties */ g_object_class_install_property (object_class, PROP_HW_ADDRESS, g_param_spec_string (NM_DEVICE_BT_HW_ADDRESS, "Bluetooth address", "Bluetooth address", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_BT_NAME, g_param_spec_string (NM_DEVICE_BT_NAME, "Bluetooth device name", "Bluetooth device name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_BT_CAPABILITIES, g_param_spec_uint (NM_DEVICE_BT_CAPABILITIES, "Bluetooth device capabilities", "Bluetooth device capabilities", NM_BT_CAPABILITY_NONE, G_MAXUINT, NM_BT_CAPABILITY_NONE, 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 (NMDeviceBtClass, 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 (NMDeviceBtClass, properties_changed)); dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_bt_object_info); dbus_g_error_domain_register (NM_BT_ERROR, NULL, NM_TYPE_BT_ERROR); }