diff options
| author | Michael Biebl <biebl@debian.org> | 2011-04-21 10:42:53 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2011-04-21 10:42:53 +0200 |
| commit | 8baa1aca8dfd35e3190d7d5655eb83b5b901e263 (patch) | |
| tree | a9ac1e67d0c22a48330c76f08fc5291f85307055 /src/nm-device-wifi.c | |
| parent | f75dd6fd1975146623052b843b182dc32c3fbe46 (diff) | |
Imported Upstream version 0.8.4.0 upstream/0.8.4.0
Diffstat (limited to 'src/nm-device-wifi.c')
| -rw-r--r-- | src/nm-device-wifi.c | 1051 |
1 files changed, 610 insertions, 441 deletions
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index 626c2c0d..2e278c7c 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -15,7 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright (C) 2005 - 2011 Red Hat, Inc. + * Copyright (C) 2005 - 2010 Red Hat, Inc. * Copyright (C) 2006 - 2008 Novell, Inc. */ @@ -104,18 +104,43 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -#define SUP_SIG_ID_LEN 5 +typedef enum { + NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, + NM_WIFI_ERROR_CONNECTION_INVALID, + NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, +} NMWifiError; + +#define NM_WIFI_ERROR (nm_wifi_error_quark ()) +#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) + +typedef struct SupplicantStateTask { + NMDeviceWifi *self; + guint32 new_state; + guint32 old_state; + gboolean mgr_task; + guint source_id; +} SupplicantStateTask; typedef struct Supplicant { NMSupplicantManager *mgr; NMSupplicantInterface *iface; - guint sig_ids[SUP_SIG_ID_LEN]; + /* signal handler ids */ + guint mgr_state_id; guint iface_error_id; + guint iface_state_id; + guint iface_scanned_ap_id; + guint iface_scan_request_result_id; + guint iface_scan_results_id; + guint iface_con_state_id; + guint iface_notify_scanning_id; /* Timeouts and idles */ guint iface_con_error_cb_id; guint con_timeout_id; + + GSList *mgr_tasks; + GSList *iface_tasks; } Supplicant; struct _NMDeviceWifiPrivate { @@ -166,23 +191,40 @@ static void schedule_scan (NMDeviceWifi *self, gboolean backoff); static void cancel_pending_scan (NMDeviceWifi *self); +static int wireless_qual_to_percent (const struct iw_quality *qual, + const struct iw_quality *max_qual); + static void cleanup_association_attempt (NMDeviceWifi * self, gboolean disconnect); static void remove_supplicant_timeouts (NMDeviceWifi *self); -static void supplicant_iface_state_cb (NMSupplicantInterface *iface, +static void supplicant_iface_state_cb (NMSupplicantInterface * iface, guint32 new_state, guint32 old_state, - gpointer user_data); + NMDeviceWifi *self); + +static void supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self); + +static void supplicant_iface_scanned_ap_cb (NMSupplicantInterface * iface, + GHashTable *properties, + NMDeviceWifi * self); -static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface, - GHashTable *properties, - NMDeviceWifi * self); +static void supplicant_iface_scan_request_result_cb (NMSupplicantInterface * iface, + gboolean success, + NMDeviceWifi * self); -static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface, - gboolean success, - NMDeviceWifi * self); +static void supplicant_iface_scan_results_cb (NMSupplicantInterface * iface, + guint32 num_bssids, + NMDeviceWifi * self); + +static void supplicant_mgr_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self); static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface, GParamSpec * pspec, @@ -192,18 +234,6 @@ static guint32 nm_device_wifi_get_bitrate (NMDeviceWifi *self); static void cull_scan_list (NMDeviceWifi *self); -/*****************************************************************/ - -typedef enum { - NM_WIFI_ERROR_CONNECTION_NOT_WIRELESS = 0, - NM_WIFI_ERROR_CONNECTION_INVALID, - NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, - NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, -} NMWifiError; - -#define NM_WIFI_ERROR (nm_wifi_error_quark ()) -#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ()) - static GQuark nm_wifi_error_quark (void) { @@ -229,8 +259,6 @@ nm_wifi_error_get_type (void) ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INVALID, "ConnectionInvalid"), /* Connection does not apply to this device. */ ENUM_ENTRY (NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE, "ConnectionIncompatible"), - /* Given access point was not in this device's scan list. */ - ENUM_ENTRY (NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, "AccessPointNotFound"), { 0, 0, 0 } }; etype = g_enum_register_static ("NMWifiError", values); @@ -238,8 +266,6 @@ nm_wifi_error_get_type (void) return etype; } -/*****************************************************************/ - /* IPW rfkill handling (until 2.6.33) */ RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self) @@ -298,109 +324,6 @@ ipw_rfkill_state_work (gpointer user_data) return TRUE; } -/*****************************************************************/ - -/* - * wireless_qual_to_percent - * - * Convert an iw_quality structure from SIOCGIWSTATS into a magical signal - * strength percentage. - * - */ -static int -wireless_qual_to_percent (const struct iw_quality *qual, - const struct iw_quality *max_qual) -{ - int percent = -1; - int level_percent = -1; - - g_return_val_if_fail (qual != NULL, -1); - g_return_val_if_fail (max_qual != NULL, -1); - - nm_log_dbg (LOGD_WIFI, - "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X", - (__s8) qual->qual, qual->qual, qual->qual, - (__s8) qual->level, qual->level, qual->level, - (__s8) qual->noise, qual->noise, qual->noise, - qual->updated, - (__s8) max_qual->qual, max_qual->qual, max_qual->qual, - (__s8) max_qual->level, max_qual->level, max_qual->level, - (__s8) max_qual->noise, max_qual->noise, max_qual->noise, - max_qual->updated); - - /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is. - * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be - * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers - * are free to use whatever they want to calculate "Link Quality". - */ - if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID)) - percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual)); - - /* If the driver doesn't specify a complete and valid quality, we have two options: - * - * 1) dBm: driver must specify max_qual->level = 0, and have valid values for - * qual->level and (qual->noise OR max_qual->noise) - * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for - * qual->level and max_qual->level - * - * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise, - * If drivers don't conform to it, they are wrong and need to be fixed. - */ - - if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */ - && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */ - && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */ - || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */ - ) { - /* Absolute power values (dBm) */ - - /* Reasonable fallbacks for dumb drivers that don't specify either level. */ - #define FALLBACK_NOISE_FLOOR_DBM -90 - #define FALLBACK_SIGNAL_MAX_DBM -20 - int max_level = FALLBACK_SIGNAL_MAX_DBM; - int noise = FALLBACK_NOISE_FLOOR_DBM; - int level = qual->level - 0x100; - - level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); - - if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)) - noise = qual->noise - 0x100; - else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) - noise = max_qual->noise - 0x100; - noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); - - /* A sort of signal-to-noise ratio calculation */ - level_percent = (int)(100 - 70 *( - ((double)max_level - (double)level) / - ((double)max_level - (double)noise))); - nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", - level_percent, max_level, level, noise); - } else if ( (max_qual->level != 0) - && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */ - && !(qual->updated & IW_QUAL_LEVEL_INVALID)) { - /* Relative power values (RSSI) */ - - int level = qual->level; - - /* Signal level is relavtive (0 -> max_qual->level) */ - level = CLAMP (level, 0, max_qual->level); - level_percent = (int)(100 * ((double)level / (double)max_qual->level)); - nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.", - level_percent, max_qual->level, level); - } else if (percent == -1) { - nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy."); - } - - /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */ - if ((percent < 1) && (level_percent >= 0)) - percent = level_percent; - - nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).", - percent, CLAMP (percent, 0, 100)); - return (CLAMP (percent, 0, 100)); -} - - /* * nm_device_wifi_update_signal_strength * @@ -702,7 +625,10 @@ constructor (GType type, /* Connect to the supplicant manager */ priv->supplicant.mgr = nm_supplicant_manager_get (); - g_assert (priv->supplicant.mgr); + priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr, + "state", + G_CALLBACK (supplicant_mgr_state_cb), + self); /* The ipw2x00 drivers don't integrate with the kernel rfkill subsystem until * 2.6.33. Thus all our nice libgudev magic is useless. So we get to poll. @@ -731,13 +657,17 @@ static gboolean supplicant_interface_acquire (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - guint id, i = 0; + guint id, mgr_state; g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (priv->supplicant.mgr != NULL, FALSE); /* interface already acquired? */ g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE); - priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr, + mgr_state = nm_supplicant_manager_get_state (priv->supplicant.mgr); + g_return_val_if_fail (mgr_state == NM_SUPPLICANT_MANAGER_STATE_IDLE, FALSE); + + priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr, nm_device_get_iface (NM_DEVICE (self)), TRUE); if (priv->supplicant.iface == NULL) { @@ -746,36 +676,70 @@ supplicant_interface_acquire (NMDeviceWifi *self) return FALSE; } - memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids)); - id = g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_STATE, + "state", G_CALLBACK (supplicant_iface_state_cb), self); - priv->supplicant.sig_ids[i++] = id; + priv->supplicant.iface_state_id = id; id = g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_NEW_BSS, - G_CALLBACK (supplicant_iface_new_bss_cb), + "scanned-ap", + G_CALLBACK (supplicant_iface_scanned_ap_cb), self); - priv->supplicant.sig_ids[i++] = id; + priv->supplicant.iface_scanned_ap_id = id; id = g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_SCAN_DONE, - G_CALLBACK (supplicant_iface_scan_done_cb), + "scan-req-result", + G_CALLBACK (supplicant_iface_scan_request_result_cb), self); - priv->supplicant.sig_ids[i++] = id; + priv->supplicant.iface_scan_request_result_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "scan-results", + G_CALLBACK (supplicant_iface_scan_results_cb), + self); + priv->supplicant.iface_scan_results_id = id; + + id = g_signal_connect (priv->supplicant.iface, + "connection-state", + G_CALLBACK (supplicant_iface_connection_state_cb), + self); + priv->supplicant.iface_con_state_id = id; id = g_signal_connect (priv->supplicant.iface, "notify::scanning", G_CALLBACK (supplicant_iface_notify_scanning_cb), self); - priv->supplicant.sig_ids[i++] = id; + priv->supplicant.iface_notify_scanning_id = id; return TRUE; } static void +finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source) +{ + NMDeviceWifi *self = task->self; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + /* idle/timeout handlers should pass FALSE for remove_source, since they + * will tell glib to remove their source from the mainloop by returning + * FALSE when they exit. When called from this NMDevice's dispose handler, + * remove_source should be TRUE to cancel all outstanding idle/timeout + * handlers asynchronously. + */ + if (task->source_id && remove_source) + g_source_remove (task->source_id); + + if (task->mgr_task) + priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task); + + memset (task, 0, sizeof (SupplicantStateTask)); + g_slice_free (SupplicantStateTask, task); +} + +static void remove_supplicant_interface_error_handler (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); @@ -798,7 +762,6 @@ static void supplicant_interface_release (NMDeviceWifi *self) { NMDeviceWifiPrivate *priv; - guint i; g_return_if_fail (self != NULL); @@ -814,34 +777,47 @@ supplicant_interface_release (NMDeviceWifi *self) remove_supplicant_interface_error_handler (self); - /* Clear supplicant interface signal handlers */ - for (i = 0; i < SUP_SIG_ID_LEN; i++) { - if (priv->supplicant.sig_ids[i] > 0) - g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.sig_ids[i]); + /* Clean up all pending supplicant interface state idle tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + + if (priv->supplicant.iface_state_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id); + priv->supplicant.iface_state_id = 0; } - memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids)); - if (priv->supplicant.iface) { - /* Tell the supplicant to disconnect from the current AP */ - nm_supplicant_interface_disconnect (priv->supplicant.iface); + if (priv->supplicant.iface_scanned_ap_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scanned_ap_id); + priv->supplicant.iface_scanned_ap_id = 0; + } - nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface); - priv->supplicant.iface = NULL; + if (priv->supplicant.iface_scan_request_result_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_request_result_id); + priv->supplicant.iface_scan_request_result_id = 0; } -} + if (priv->supplicant.iface_scan_results_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_results_id); + priv->supplicant.iface_scan_results_id = 0; + } -static NMAccessPoint * -get_ap_by_path (NMDeviceWifi *self, const char *path) -{ - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - GSList *iter; + if (priv->supplicant.iface_con_state_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id); + priv->supplicant.iface_con_state_id = 0; + } - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - if (strcmp (path, nm_ap_get_dbus_path (NM_AP (iter->data))) == 0) - return NM_AP (iter->data); + if (priv->supplicant.iface_notify_scanning_id > 0) { + g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_notify_scanning_id); + priv->supplicant.iface_notify_scanning_id = 0; + } + + if (priv->supplicant.iface) { + /* Tell the supplicant to disconnect from the current AP */ + nm_supplicant_interface_disconnect (priv->supplicant.iface); + + nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface); + priv->supplicant.iface = NULL; } - return NULL; } static NMAccessPoint * @@ -1250,7 +1226,7 @@ real_take_down (NMDevice *dev) } static void -real_deactivate (NMDevice *dev) +real_deactivate_quickly (NMDevice *dev) { NMDeviceWifi *self = NM_DEVICE_WIFI (dev); NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); @@ -1286,11 +1262,16 @@ real_deactivate (NMDevice *dev) /* Reset MAC address back to initial address */ _set_hw_addr (self, priv->initial_hw_addr, "reset"); +} + +static void +real_deactivate (NMDevice *dev) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (dev); - /* Ensure we're in infrastructure mode after deactivation; some devices - * (usually older ones) don't scan well in adhoc mode. - */ nm_device_wifi_set_mode (self, NM_802_11_MODE_INFRA); + /* FIXME: Should we reset the scan interval here? */ +/* nm_device_wifi_set_scan_interval (app_data, self, NM_WIRELESS_SCAN_INTERVAL_ACTIVE); */ } static gboolean @@ -1337,170 +1318,6 @@ real_check_connection_compatible (NMDevice *device, return TRUE; } -/* - * List of manufacturer default SSIDs that are often unchanged by users. - * - * NOTE: this list should *not* contain networks that you would like to - * automatically roam to like "Starbucks" or "AT&T" or "T-Mobile HotSpot". - */ -static const char * -manf_defaults[] = { - "linksys", - "linksys-a", - "linksys-g", - "default", - "belkin54g", - "NETGEAR", - "o2DSL", - "WLAN", - "ALICE-WLAN", -}; - -#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) - -static gboolean -is_manf_default_ssid (const GByteArray *ssid) -{ - int i; - - for (i = 0; i < ARRAY_SIZE (manf_defaults); i++) { - if (ssid->len == strlen (manf_defaults[i])) { - if (memcmp (manf_defaults[i], ssid->data, ssid->len) == 0) - return TRUE; - } - } - return FALSE; -} - -static gboolean -real_complete_connection (NMDevice *device, - NMConnection *connection, - const char *specific_object, - const GSList *existing_connections, - GError **error) -{ - NMDeviceWifi *self = NM_DEVICE_WIFI (device); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMSettingWireless *s_wifi; - NMSettingWirelessSecurity *s_wsec; - NMSetting8021x *s_8021x; - const GByteArray *setting_mac; - char *format, *str_ssid = NULL; - NMAccessPoint *ap = NULL; - const GByteArray *ssid = NULL; - GSList *iter; - - s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); - s_wsec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY); - s_8021x = (NMSetting8021x *) nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X); - - if (!specific_object) { - /* If not given a specific object, we need at minimum an SSID */ - if (!s_wifi) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INVALID, - "A 'wireless' setting is required if no AP path was given."); - return FALSE; - } - - ssid = nm_setting_wireless_get_ssid (s_wifi); - if (!ssid || !ssid->len) { - g_set_error_literal (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_CONNECTION_INVALID, - "A 'wireless' setting with a valid SSID is required if no AP path was given."); - return FALSE; - } - - /* Find a compatible AP in the scan list */ - for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) { - if (nm_ap_check_compatible (NM_AP (iter->data), connection)) { - ap = NM_AP (iter->data); - break; - } - } - - /* If we still don't have an AP, then the WiFI settings needs to be - * fully specified by the client. Might not be able to find an AP - * if the network isn't broadcasting the SSID for example. - */ - if (!ap) { - GSList *settings = NULL; - gboolean valid; - - settings = g_slist_prepend (settings, s_wifi); - settings = g_slist_prepend (settings, s_wsec); - settings = g_slist_prepend (settings, s_8021x); - valid = nm_setting_verify (NM_SETTING (s_wifi), settings, error); - g_slist_free (settings); - if (!valid) - return FALSE; - } - } else { - ap = get_ap_by_path (self, specific_object); - if (!ap) { - g_set_error (error, - NM_WIFI_ERROR, - NM_WIFI_ERROR_ACCESS_POINT_NOT_FOUND, - "The access point %s was not in the scan list.", - specific_object); - return FALSE; - } - } - - if (ap) { - ssid = nm_ap_get_ssid (ap); - - /* If the SSID is a well-known SSID, lock the connection to the AP's - * specific BSSID so NM doesn't autoconnect to some random wifi net. - */ - if (!nm_ap_complete_connection (ap, - connection, - is_manf_default_ssid (ssid), - error)) - return FALSE; - } - - g_assert (ssid); - str_ssid = nm_utils_ssid_to_utf8 (ssid); - format = g_strdup_printf ("%s %%d", str_ssid); - - nm_utils_complete_generic (connection, - NM_SETTING_WIRELESS_SETTING_NAME, - existing_connections, - format, - str_ssid, - TRUE); - g_free (str_ssid); - g_free (format); - - setting_mac = nm_setting_wireless_get_mac_address (s_wifi); - if (setting_mac) { - /* Make sure the setting MAC (if any) matches the device's permanent MAC */ - if (memcmp (setting_mac->data, priv->perm_hw_addr, ETH_ALEN)) { - g_set_error (error, - NM_SETTING_WIRELESS_ERROR, - NM_SETTING_WIRELESS_ERROR_INVALID_PROPERTY, - NM_SETTING_WIRELESS_MAC_ADDRESS); - return FALSE; - } - } else { - GByteArray *mac; - const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - - /* Lock the connection to this device by default */ - if (memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) { - mac = g_byte_array_sized_new (ETH_ALEN); - g_byte_array_append (mac, priv->perm_hw_addr, ETH_ALEN); - g_object_set (G_OBJECT (s_wifi), NM_SETTING_WIRELESS_MAC_ADDRESS, mac, NULL); - g_byte_array_free (mac, TRUE); - } - } - - return TRUE; -} - static gboolean real_is_available (NMDevice *dev) { @@ -1774,6 +1591,107 @@ nm_device_wifi_get_frequency (NMDeviceWifi *self) } /* + * wireless_stats_to_percent + * + * Convert an iw_stats structure from a scan or the card into + * a magical signal strength percentage. + * + */ +static int +wireless_qual_to_percent (const struct iw_quality *qual, + const struct iw_quality *max_qual) +{ + int percent = -1; + int level_percent = -1; + + g_return_val_if_fail (qual != NULL, -1); + g_return_val_if_fail (max_qual != NULL, -1); + + nm_log_dbg (LOGD_WIFI, + "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X", + (__s8) qual->qual, qual->qual, qual->qual, + (__s8) qual->level, qual->level, qual->level, + (__s8) qual->noise, qual->noise, qual->noise, + qual->updated, + (__s8) max_qual->qual, max_qual->qual, max_qual->qual, + (__s8) max_qual->level, max_qual->level, max_qual->level, + (__s8) max_qual->noise, max_qual->noise, max_qual->noise, + max_qual->updated); + + /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is. + * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be + * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers + * are free to use whatever they want to calculate "Link Quality". + */ + if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID)) + percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual)); + + /* If the driver doesn't specify a complete and valid quality, we have two options: + * + * 1) dBm: driver must specify max_qual->level = 0, and have valid values for + * qual->level and (qual->noise OR max_qual->noise) + * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for + * qual->level and max_qual->level + * + * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise, + * If drivers don't conform to it, they are wrong and need to be fixed. + */ + + if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */ + && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */ + && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */ + || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */ + ) { + /* Absolute power values (dBm) */ + + /* Reasonable fallbacks for dumb drivers that don't specify either level. */ + #define FALLBACK_NOISE_FLOOR_DBM -90 + #define FALLBACK_SIGNAL_MAX_DBM -20 + int max_level = FALLBACK_SIGNAL_MAX_DBM; + int noise = FALLBACK_NOISE_FLOOR_DBM; + int level = qual->level - 0x100; + + level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); + + if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID)) + noise = qual->noise - 0x100; + else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) + noise = max_qual->noise - 0x100; + noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM); + + /* A sort of signal-to-noise ratio calculation */ + level_percent = (int)(100 - 70 *( + ((double)max_level - (double)level) / + ((double)max_level - (double)noise))); + nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.", + level_percent, max_level, level, noise); + } else if ( (max_qual->level != 0) + && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */ + && !(qual->updated & IW_QUAL_LEVEL_INVALID)) { + /* Relative power values (RSSI) */ + + int level = qual->level; + + /* Signal level is relavtive (0 -> max_qual->level) */ + level = CLAMP (level, 0, max_qual->level); + level_percent = (int)(100 * ((double)level / (double)max_qual->level)); + nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.", + level_percent, max_qual->level, level); + } else if (percent == -1) { + nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy."); + } + + /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */ + if ((percent < 1) && (level_percent >= 0)) + percent = level_percent; + + nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).", + percent, CLAMP (percent, 0, 100)); + return (CLAMP (percent, 0, 100)); +} + + +/* * nm_device_wifi_get_ssid * * If a device is wireless, return the ssid that it is attempting @@ -1910,9 +1828,6 @@ scanning_allowed (NMDeviceWifi *self) case NM_DEVICE_STATE_CONFIG: case NM_DEVICE_STATE_NEED_AUTH: case NM_DEVICE_STATE_IP_CONFIG: - case NM_DEVICE_STATE_IP_CHECK: - case NM_DEVICE_STATE_SECONDARIES: - case NM_DEVICE_STATE_DEACTIVATING: /* Don't scan when unusable or activating */ return FALSE; case NM_DEVICE_STATE_DISCONNECTED: @@ -1925,11 +1840,11 @@ scanning_allowed (NMDeviceWifi *self) } /* Don't scan if the supplicant is busy */ - sup_state = nm_supplicant_interface_get_state (priv->supplicant.iface); - if ( sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING - || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED - || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE - || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE + sup_state = nm_supplicant_interface_get_connection_state (priv->supplicant.iface); + if ( sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING + || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED + || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE + || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE || nm_supplicant_interface_get_scanning (priv->supplicant.iface)) return FALSE; @@ -2085,19 +2000,28 @@ cancel_pending_scan (NMDeviceWifi *self) } } + static void -supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, - gboolean success, - NMDeviceWifi *self) +supplicant_iface_scan_request_result_cb (NMSupplicantInterface *iface, + gboolean success, + NMDeviceWifi *self) { - nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan %s", + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan request %s", nm_device_get_iface (NM_DEVICE (self)), success ? "successful" : "failed"); if (check_scanning_allowed (self)) schedule_scan (self, TRUE); +} -#if 0 +static void +supplicant_iface_scan_results_cb (NMSupplicantInterface *iface, + guint32 num_results, + NMDeviceWifi *self) +{ + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan results available (%d APs found)", + nm_device_get_iface (NM_DEVICE (self)), + num_results); if (num_results == 0) { /* ensure that old APs get culled, which otherwise only * happens when there are actual scan results to process. @@ -2105,7 +2029,6 @@ supplicant_iface_scan_done_cb (NMSupplicantInterface *iface, cull_scan_list (self); nm_device_wifi_ap_list_print (self); } -#endif } static gboolean @@ -2325,10 +2248,48 @@ cull_scan_list (NMDeviceWifi *self) removed, total); } +#define SET_QUALITY_MEMBER(qual_item, lc_member, uc_member) \ + if (lc_member != -1) { \ + qual_item.lc_member = lc_member; \ + qual_item.updated |= IW_QUAL_##uc_member##_UPDATED; \ + } else { \ + qual_item.updated |= IW_QUAL_##uc_member##_INVALID; \ + } + +static void +set_ap_strength_from_properties (NMDeviceWifi *self, + NMAccessPoint *ap, + GHashTable *properties) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + int qual, level, noise; + struct iw_quality quality; + GValue *value; + gint8 strength; + + value = (GValue *) g_hash_table_lookup (properties, "quality"); + qual = value ? g_value_get_int (value) : -1; + + value = (GValue *) g_hash_table_lookup (properties, "level"); + level = value ? g_value_get_int (value) : -1; + + value = (GValue *) g_hash_table_lookup (properties, "noise"); + noise = value ? g_value_get_int (value) : -1; + + /* Calculate and set the AP's signal quality */ + memset (&quality, 0, sizeof (struct iw_quality)); + SET_QUALITY_MEMBER (quality, qual, QUAL); + SET_QUALITY_MEMBER (quality, level, LEVEL); + SET_QUALITY_MEMBER (quality, noise, NOISE); + + strength = wireless_qual_to_percent (&quality, &priv->max_qual); + nm_ap_set_strength (ap, strength); +} + static void -supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, - GHashTable *properties, - NMDeviceWifi *self) +supplicant_iface_scanned_ap_cb (NMSupplicantInterface *iface, + GHashTable *properties, + NMDeviceWifi *self) { NMDeviceState state; NMAccessPoint *ap; @@ -2344,6 +2305,8 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface, ap = nm_ap_new_from_properties (properties); if (ap) { + set_ap_strength_from_properties (self, ap, properties); + nm_ap_print_self (ap, "AP: "); /* Add the AP to the device's AP list */ @@ -2372,27 +2335,6 @@ cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect) nm_supplicant_interface_disconnect (priv->supplicant.iface); } -static void -wifi_secrets_cb (NMActRequest *req, - guint32 call_id, - NMConnection *connection, - GError *error, - gpointer user_data) -{ - NMDevice *dev = NM_DEVICE (user_data); - - g_return_if_fail (req == nm_device_get_act_request (dev)); - g_return_if_fail (nm_device_get_state (dev) == NM_DEVICE_STATE_NEED_AUTH); - g_return_if_fail (nm_act_request_get_connection (req) == connection); - - if (error) { - nm_log_warn (LOGD_WIFI, "%s", error->message); - nm_device_state_changed (dev, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); - } else - nm_device_activate_schedule_stage1_device_prepare (dev); -} static void remove_link_timeout (NMDeviceWifi *self) @@ -2489,10 +2431,10 @@ link_timeout_cb (gpointer user_data) nm_device_state_changed (dev, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); nm_act_request_get_secrets (req, setting_name, - NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW, + TRUE, + SECRETS_CALLER_WIFI, NULL, - wifi_secrets_cb, - self); + NULL); return FALSE; } @@ -2502,97 +2444,253 @@ time_out: return FALSE; } -static void -supplicant_iface_state_cb (NMSupplicantInterface *iface, - guint32 new_state, - guint32 old_state, - gpointer user_data) +static gboolean +schedule_state_handler (NMDeviceWifi *self, + GSourceFunc handler, + guint32 new_state, + guint32 old_state, + gboolean mgr_task) { - NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); - NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - NMDevice *device = NM_DEVICE (self); - NMDeviceState devstate; - gboolean scanning; + NMDeviceWifiPrivate *priv; + SupplicantStateTask *task; + + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (handler != NULL, FALSE); if (new_state == old_state) - return; + return TRUE; - nm_log_info (LOGD_DEVICE | LOGD_WIFI, - "(%s): supplicant interface state: %s -> %s", - nm_device_get_iface (device), - nm_supplicant_interface_state_to_string (old_state), - nm_supplicant_interface_state_to_string (new_state)); + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); - devstate = nm_device_get_state (device); - scanning = nm_supplicant_interface_get_scanning (iface); + task = g_slice_new0 (SupplicantStateTask); + if (!task) { + nm_log_err (LOGD_WIFI, "Not enough memory to process supplicant manager state change."); + return FALSE; + } - switch (new_state) { - case NM_SUPPLICANT_INTERFACE_STATE_READY: + task->self = self; + task->new_state = new_state; + task->old_state = old_state; + task->mgr_task = mgr_task; + + task->source_id = g_idle_add (handler, task); + if (mgr_task) + priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task); + else + priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task); + + return TRUE; +} + +static gboolean +supplicant_iface_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + + g_return_val_if_fail (task != NULL, FALSE); + + self = task->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + + nm_log_info (LOGD_WIFI, "(%s): supplicant interface state: %s -> %s", + nm_device_get_iface (NM_DEVICE (self)), + nm_supplicant_interface_state_to_string (task->old_state), + nm_supplicant_interface_state_to_string (task->new_state)); + + if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) { priv->scan_interval = SCAN_INTERVAL_MIN; /* If the interface can now be activated because the supplicant is now * available, transition to DISCONNECTED. */ - if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) { - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, + if ( (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_UNAVAILABLE) + && nm_device_is_available (NM_DEVICE (self))) { + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE); } - nm_log_dbg (LOGD_WIFI_SCAN, - "(%s): supplicant ready, requesting initial scan", - nm_device_get_iface (device)); + nm_log_dbg (LOGD_WIFI_SCAN, "(%s): supplicant ready, requesting initial scan", + nm_device_get_iface (NM_DEVICE (self))); /* Request a scan to get latest results */ cancel_pending_scan (self); request_wireless_scan (self); - break; - case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED: + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) { + cleanup_association_attempt (self, FALSE); + supplicant_interface_release (self); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + + +static void +supplicant_iface_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + schedule_state_handler (self, + supplicant_iface_state_cb_handler, + new_state, + old_state, + FALSE); +} + + +static gboolean +supplicant_iface_connection_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self = task->self; + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMDevice *dev = NM_DEVICE (self); + gboolean scanning; + + if (!nm_device_get_act_request (dev)) { + /* The device is not activating or already activated; do nothing. */ + goto out; + } + + nm_log_info (LOGD_WIFI, "(%s): supplicant connection state: %s -> %s", + nm_device_get_iface (dev), + nm_supplicant_interface_connection_state_to_string (task->old_state), + nm_supplicant_interface_connection_state_to_string (task->new_state)); + + scanning = nm_supplicant_interface_get_scanning (priv->supplicant.iface); + + if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) { remove_supplicant_interface_error_handler (self); remove_supplicant_timeouts (self); /* If this is the initial association during device activation, * schedule the next activation stage. */ - if (devstate == NM_DEVICE_STATE_CONFIG) { + if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) { NMAccessPoint *ap = nm_device_wifi_get_activation_ap (self); - const GByteArray *ssid = nm_ap_get_ssid (ap); + const GByteArray * ssid = nm_ap_get_ssid (ap); nm_log_info (LOGD_DEVICE | LOGD_WIFI, "Activation (%s/wireless) Stage 2 of 5 (Device Configure) " "successful. Connected to wireless network '%s'.", - nm_device_get_iface (device), + nm_device_get_iface (dev), ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)"); - nm_device_activate_schedule_stage3_ip_config_start (device); + nm_device_activate_schedule_stage3_ip_config_start (dev); } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED: - if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) { + } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) { + if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) { /* Start the link timeout so we allow some time for reauthentication, * use a longer timeout if we are scanning since some cards take a * while to scan. */ if (!priv->link_timeout_id) { priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15, - link_timeout_cb, self); + link_timeout_cb, self); } } - break; - case NM_SUPPLICANT_INTERFACE_STATE_DOWN: - cleanup_association_attempt (self, FALSE); - supplicant_interface_release (self); - nm_device_state_changed (device, - NM_DEVICE_STATE_UNAVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - break; - default: - break; } +out: /* Signal scanning state changes */ - if ( new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING - || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING) + if ( task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING + || task->old_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING) g_object_notify (G_OBJECT (self), "scanning"); + + finish_supplicant_task (task, FALSE); + return FALSE; +} + + +static void +supplicant_iface_connection_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + schedule_state_handler (self, + supplicant_iface_connection_state_cb_handler, + new_state, + old_state, + FALSE); +} + + +static gboolean +supplicant_mgr_state_cb_handler (gpointer user_data) +{ + SupplicantStateTask *task = (SupplicantStateTask *) user_data; + NMDeviceWifi *self; + NMDeviceWifiPrivate *priv; + NMDevice *dev; + NMDeviceState dev_state; + + g_return_val_if_fail (task != NULL, FALSE); + + self = task->self; + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + dev = NM_DEVICE (self); + + nm_log_info (LOGD_WIFI, "(%s): supplicant manager state: %s -> %s", + nm_device_get_iface (NM_DEVICE (self)), + nm_supplicant_manager_state_to_string (task->old_state), + nm_supplicant_manager_state_to_string (task->new_state)); + + /* If the supplicant went away, release the supplicant interface */ + if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) { + if (priv->supplicant.iface) { + cleanup_association_attempt (self, FALSE); + supplicant_interface_release (self); + } + + if (nm_device_get_state (dev) > NM_DEVICE_STATE_UNAVAILABLE) { + nm_device_state_changed (dev, NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + } + } else if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) { + dev_state = nm_device_get_state (dev); + if ( priv->enabled + && !priv->supplicant.iface + && (dev_state >= NM_DEVICE_STATE_UNAVAILABLE) + && (nm_device_get_firmware_missing (NM_DEVICE (self)) == FALSE)) { + /* request a supplicant interface from the supplicant manager */ + supplicant_interface_acquire (self); + + /* if wireless is enabled and we have a supplicant interface, + * we can transition to the DISCONNECTED state. + */ + if (priv->supplicant.iface) { + nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_NONE); + } + } + } + + finish_supplicant_task (task, FALSE); + return FALSE; +} + +static void +supplicant_mgr_state_cb (NMSupplicantInterface * iface, + guint32 new_state, + guint32 old_state, + NMDeviceWifi *self) +{ + g_return_if_fail (self != NULL); + + schedule_state_handler (self, + supplicant_mgr_state_cb_handler, + new_state, + old_state, + TRUE); } struct iface_con_error_cb_data { @@ -2704,7 +2802,6 @@ handle_auth_or_fail (NMDeviceWifi *self, guint32 tries; NMAccessPoint *ap; NMConnection *connection; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE); @@ -2728,21 +2825,24 @@ handle_auth_or_fail (NMDeviceWifi *self, nm_connection_clear_secrets (connection); setting_name = nm_connection_need_secrets (connection, NULL); if (setting_name) { - NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION; + gboolean get_new; /* If the caller doesn't necessarily want completely new secrets, * only ask for new secrets after the first failure. */ - if (new_secrets || tries) - flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW; - nm_act_request_get_secrets (req, setting_name, flags, NULL, wifi_secrets_cb, self); + get_new = new_secrets ? TRUE : (tries ? TRUE : FALSE); + nm_act_request_get_secrets (req, + setting_name, + get_new, + SECRETS_CALLER_WIFI, + NULL, + NULL); g_object_set_data (G_OBJECT (connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries)); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else + } else { nm_log_warn (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); - - return ret; + } + return NM_ACT_STAGE_RETURN_POSTPONE; } /* @@ -3112,6 +3212,42 @@ done: return NM_ACT_STAGE_RETURN_SUCCESS; } + +static void +real_connection_secrets_updated (NMDevice *dev, + NMConnection *connection, + GSList *updated_settings, + RequestSecretsCaller caller) +{ + NMActRequest *req; + gboolean valid = FALSE; + GSList *iter; + + g_return_if_fail (caller == SECRETS_CALLER_WIFI); + + if (nm_device_get_state (dev) != NM_DEVICE_STATE_NEED_AUTH) + return; + + for (iter = updated_settings; iter; iter = g_slist_next (iter)) { + const char *setting_name = (const char *) iter->data; + + if ( !strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) + || !strcmp (setting_name, NM_SETTING_802_1X_SETTING_NAME)) { + valid = TRUE; + } else { + nm_log_warn (LOGD_DEVICE, "Ignoring updated secrets for setting '%s'.", + setting_name); + } + } + + 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 NMActStageReturn real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) { @@ -3124,6 +3260,7 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) NMActRequest *req; NMAccessPoint *ap; NMConnection *connection; + NMSettingConnection *s_connection; const char *setting_name; NMSettingWireless *s_wireless; @@ -3140,6 +3277,9 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) connection = nm_act_request_get_connection (req); g_assert (connection); + s_connection = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + g_assert (s_connection); + s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS); g_assert (s_wireless); @@ -3149,7 +3289,7 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) nm_log_info (LOGD_DEVICE | LOGD_WIFI, "Activation (%s/wireless): access point '%s' has security," " but secrets are required.", - iface, nm_connection_get_id (connection)); + iface, nm_setting_connection_get_id (s_connection)); ret = handle_auth_or_fail (self, req, FALSE); if (ret == NM_ACT_STAGE_RETURN_FAILURE) @@ -3162,12 +3302,12 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) nm_log_info (LOGD_DEVICE | LOGD_WIFI, "Activation (%s/wireless): connection '%s' has security" ", and secrets exist. No new secrets needed.", - iface, nm_connection_get_id (connection)); + iface, nm_setting_connection_get_id (s_connection)); } else { nm_log_info (LOGD_DEVICE | LOGD_WIFI, "Activation (%s/wireless): connection '%s' requires no " "security. No secrets needed.", - iface, nm_connection_get_id (connection)); + iface, nm_setting_connection_get_id (s_connection)); } config = build_supplicant_config (self, connection, ap); @@ -3181,7 +3321,7 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason) /* Hook up error signal handler to capture association errors */ id = g_signal_connect (priv->supplicant.iface, - NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR, + "connection-error", G_CALLBACK (supplicant_iface_connection_error_cb), self); priv->supplicant.iface_error_id = id; @@ -3273,12 +3413,16 @@ handle_ip_config_timeout (NMDeviceWifi *self, */ auth_enforced = ap_auth_enforced (connection, ap, &encrypted); if (encrypted && !auth_enforced && !may_fail) { + NMSettingConnection *s_con; + + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); + /* Activation failed, we must have bad encryption key */ nm_log_warn (LOGD_DEVICE | LOGD_WIFI, "Activation (%s/wireless): could not get IP configuration for " "connection '%s'.", - nm_device_get_iface (NM_DEVICE (self)), - nm_connection_get_id (connection)); + nm_device_get_iface (NM_DEVICE (self)), + nm_setting_connection_get_id (s_con)); ret = handle_auth_or_fail (self, NULL, TRUE); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { @@ -3564,8 +3708,10 @@ device_state_changed (NMDevice *device, NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self) { + NMDeviceWifiPrivate *priv; NMActRequest *req; const char *ap_path; + GSList * elt; g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NULL); @@ -3574,8 +3720,18 @@ nm_device_wifi_get_activation_ap (NMDeviceWifi *self) return NULL; ap_path = nm_act_request_get_specific_object (req); + if (!ap_path) + return NULL; + + /* Find the AP by it's object path */ + priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + for (elt = priv->ap_list; elt; elt = g_slist_next (elt)) { + NMAccessPoint *ap = NM_AP (elt->data); - return ap_path ? get_ap_by_path (self, ap_path) : NULL; + if (!strcmp (ap_path, nm_ap_get_dbus_path (ap))) + return ap; + } + return NULL; } static void @@ -3625,11 +3781,12 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled) /* Wait for some drivers like ipw3945 to come back to life */ success = wireless_get_range (self, &range, NULL); - /* Re-initialize the supplicant interface and wait for it to be ready */ + /* iface should be NULL here, but handle it anyway if it's not */ + g_warn_if_fail (priv->supplicant.iface == NULL); if (priv->supplicant.iface) supplicant_interface_release (self); - supplicant_interface_acquire (self); + supplicant_interface_acquire (self); nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state", nm_device_get_iface (NM_DEVICE (device))); } else { @@ -3691,9 +3848,20 @@ dispose (GObject *object) priv->periodic_source_id = 0; } + /* Clean up all pending supplicant tasks */ + while (priv->supplicant.iface_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE); + while (priv->supplicant.mgr_tasks) + finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE); + cleanup_association_attempt (self, TRUE); supplicant_interface_release (self); + if (priv->supplicant.mgr_state_id) { + g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id); + priv->supplicant.mgr_state_id = 0; + } + if (priv->supplicant.mgr) { g_object_unref (priv->supplicant.mgr); priv->supplicant.mgr = NULL; @@ -3801,8 +3969,8 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->update_initial_hw_address = real_update_initial_hw_address; parent_class->get_best_auto_connection = real_get_best_auto_connection; parent_class->is_available = real_is_available; + parent_class->connection_secrets_updated = real_connection_secrets_updated; parent_class->check_connection_compatible = real_check_connection_compatible; - parent_class->complete_connection = real_complete_connection; parent_class->act_stage1_prepare = real_act_stage1_prepare; parent_class->act_stage2_config = real_act_stage2_config; @@ -3810,6 +3978,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) parent_class->act_stage4_ip4_config_timeout = real_act_stage4_ip4_config_timeout; parent_class->act_stage4_ip6_config_timeout = real_act_stage4_ip6_config_timeout; parent_class->deactivate = real_deactivate; + parent_class->deactivate_quickly = real_deactivate_quickly; parent_class->can_interrupt_activation = real_can_interrupt_activation; parent_class->spec_match_list = spec_match_list; |