diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c index a8aa6b90c4..20b73fb2fb 100644 --- a/src/nm-device-wifi.c +++ b/src/nm-device-wifi.c @@ -84,6 +84,7 @@ enum { PROP_CAPABILITIES, PROP_IFINDEX, PROP_SCANNING, + PROP_IPW_RFKILL_STATE, LAST_PROP }; @@ -145,6 +146,11 @@ struct _NMDeviceWifiPrivate { struct ether_addr hw_addr; guint32 ifindex; + /* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */ + char * ipw_rfkill_path; + guint ipw_rfkill_id; + RfKillState ipw_rfkill_state; + GByteArray * ssid; gint8 invalid_strength_counter; iwqual max_qual; @@ -260,10 +266,54 @@ nm_wifi_error_get_type (void) return etype; } -static void -access_point_removed (NMDeviceWifi *device, NMAccessPoint *ap) +/* IPW rfkill handling (until 2.6.33) */ +RfKillState +nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self) { - g_signal_emit (device, signals[ACCESS_POINT_REMOVED], 0, ap); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + char *contents = NULL; + RfKillState state = RFKILL_UNBLOCKED; + + if ( priv->ipw_rfkill_path + && g_file_get_contents (priv->ipw_rfkill_path, &contents, NULL, NULL)) { + contents = g_strstrip (contents); + + /* 0 - RF kill not enabled + * 1 - SW based RF kill active (sysfs) + * 2 - HW based RF kill active + * 3 - Both HW and SW baed RF kill active + */ + switch (contents[0]) { + case '1': + state = RFKILL_SOFT_BLOCKED; + break; + case '2': + case '3': + state = RFKILL_HARD_BLOCKED; + break; + case '0': + default: + break; + } + g_free (contents); + } + + return state; +} + +static gboolean +ipw_rfkill_state_work (gpointer user_data) +{ + NMDeviceWifi *self = NM_DEVICE_WIFI (user_data); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + RfKillState old_state; + + old_state = priv->ipw_rfkill_state; + priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self); + if (priv->ipw_rfkill_state != old_state) + g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_IPW_RFKILL_STATE); + + return TRUE; } /* @@ -560,6 +610,22 @@ constructor (GType type, 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. + * + * FIXME: when 2.6.33 comes lands, we can do some sysfs parkour to figure out + * if we need to poll or not by matching /sys/class/net/ethX/device to one + * of the /sys/class/rfkill/rfkillX/device links. If there's a match, we + * don't have to poll. + */ + priv->ipw_rfkill_path = g_strdup_printf ("/sys/class/net/%s/device/rf_kill", + nm_device_get_iface (NM_DEVICE (self))); + if (!g_file_test (priv->ipw_rfkill_path, G_FILE_TEST_IS_REGULAR)) { + g_free (priv->ipw_rfkill_path); + priv->ipw_rfkill_path = NULL; + } + priv->ipw_rfkill_state = nm_device_wifi_get_ipw_rfkill_state (self); + return object; error: @@ -1015,6 +1081,12 @@ real_bring_up (NMDevice *dev) return TRUE; } +static void +access_point_removed (NMDeviceWifi *device, NMAccessPoint *ap) +{ + g_signal_emit (device, signals[ACCESS_POINT_REMOVED], 0, ap); +} + static void remove_all_aps (NMDeviceWifi *self) { @@ -3260,6 +3332,19 @@ device_state_changed (NMDevice *device, supplicant_interface_release (self); } + /* Start or stop the rfkill poll worker for ipw cards */ + if (priv->ipw_rfkill_path) { + if (new_state > NM_DEVICE_STATE_UNMANAGED) { + if (!priv->ipw_rfkill_id) + priv->ipw_rfkill_id = g_timeout_add_seconds (3, ipw_rfkill_state_work, self); + } else if (new_state <= NM_DEVICE_STATE_UNMANAGED) { + if (priv->ipw_rfkill_id) { + g_source_remove (priv->ipw_rfkill_id); + priv->ipw_rfkill_id = 0; + } + } + } + switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: clear_aps = TRUE; @@ -3456,6 +3541,12 @@ dispose (GObject *object) set_current_ap (self, NULL); remove_all_aps (self); + g_free (priv->ipw_rfkill_path); + if (priv->ipw_rfkill_id) { + g_source_remove (priv->ipw_rfkill_id); + priv->ipw_rfkill_id = 0; + } + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); } @@ -3493,6 +3584,9 @@ get_property (GObject *object, guint prop_id, case PROP_SCANNING: g_value_set_boolean (value, nm_supplicant_interface_get_scanning (priv->supplicant.iface)); break; + case PROP_IPW_RFKILL_STATE: + g_value_set_uint (value, nm_device_wifi_get_ipw_rfkill_state (device)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3510,6 +3604,10 @@ set_property (GObject *object, guint prop_id, /* construct-only */ priv->ifindex = g_value_get_uint (value); break; + case PROP_IPW_RFKILL_STATE: + /* construct only */ + priv->ipw_rfkill_state = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -3607,6 +3705,13 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) FALSE, G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT)); + g_object_class_install_property (object_class, PROP_IPW_RFKILL_STATE, + g_param_spec_uint (NM_DEVICE_WIFI_IPW_RFKILL_STATE, + "IpwRfkillState", + "ipw rf-kill state", + RFKILL_UNBLOCKED, RFKILL_HARD_BLOCKED, RFKILL_UNBLOCKED, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | NM_PROPERTY_PARAM_NO_EXPORT)); + /* Signals */ signals[ACCESS_POINT_ADDED] = g_signal_new ("access-point-added", diff --git a/src/nm-device-wifi.h b/src/nm-device-wifi.h index d057499ab1..126bc13982 100644 --- a/src/nm-device-wifi.h +++ b/src/nm-device-wifi.h @@ -26,7 +26,7 @@ #include #include - +#include "nm-rfkill.h" #include "nm-device.h" #include "NetworkManagerAP.h" @@ -49,6 +49,7 @@ G_BEGIN_DECLS #define NM_DEVICE_WIFI_CAPABILITIES "wireless-capabilities" #define NM_DEVICE_WIFI_IFINDEX "ifindex" #define NM_DEVICE_WIFI_SCANNING "scanning" +#define NM_DEVICE_WIFI_IPW_RFKILL_STATE "ipw-rfkill-state" #ifndef NM_DEVICE_WIFI_DEFINED #define NM_DEVICE_WIFI_DEFINED @@ -105,6 +106,8 @@ void nm_device_wifi_set_enabled (NMDeviceWifi *self, gboolean enabled); guint32 nm_device_wifi_get_ifindex (NMDeviceWifi *self); +RfKillState nm_device_wifi_get_ipw_rfkill_state (NMDeviceWifi *self); + G_END_DECLS #endif /* NM_DEVICE_WIFI_H */ diff --git a/src/nm-manager.c b/src/nm-manager.c index c8a4e155b0..14c2a907e0 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -80,7 +80,6 @@ static gboolean impl_manager_legacy_state (NMManager *manager, guint32 *state, G #include "nm-manager-glue.h" static void user_destroy_connections (NMManager *manager); -static void manager_set_wireless_enabled (NMManager *manager, gboolean enabled); static void connection_added_default_handler (NMManager *manager, NMConnection *connection, @@ -95,10 +94,6 @@ static void udev_device_removed_cb (NMUdevManager *udev_mgr, GUdevDevice *device, gpointer user_data); -static void udev_manager_rfkill_changed_cb (NMUdevManager *udev_mgr, - RfKillState state, - gpointer user_data); - static void bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr, const char *bdaddr, const char *name, @@ -1256,6 +1251,80 @@ next: g_slist_free (connections); } +static RfKillState +nm_manager_get_ipw_rfkill_state (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + RfKillState ipw_state = RFKILL_UNBLOCKED; + + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = NM_DEVICE (iter->data); + RfKillState candidate_state; + + if (NM_IS_DEVICE_WIFI (candidate)) { + candidate_state = nm_device_wifi_get_ipw_rfkill_state (NM_DEVICE_WIFI (candidate)); + + if (candidate_state > ipw_state) + ipw_state = candidate_state; + } + } + + return ipw_state; +} + +static void +nm_manager_rfkill_update (NMManager *self) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + RfKillState udev_state, ipw_state, composite; + gboolean new_we = TRUE, new_whe = TRUE; + + udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr); + ipw_state = nm_manager_get_ipw_rfkill_state (self); + + /* The composite state is the "worst" of either udev or ipw states */ + if (udev_state == RFKILL_HARD_BLOCKED || ipw_state == RFKILL_HARD_BLOCKED) + composite = RFKILL_HARD_BLOCKED; + else if (udev_state == RFKILL_SOFT_BLOCKED || ipw_state == RFKILL_SOFT_BLOCKED) + composite = RFKILL_SOFT_BLOCKED; + else + composite = RFKILL_UNBLOCKED; + + switch (composite) { + case RFKILL_UNBLOCKED: + new_we = TRUE; + new_whe = TRUE; + break; + case RFKILL_SOFT_BLOCKED: + new_we = FALSE; + new_whe = TRUE; + break; + case RFKILL_HARD_BLOCKED: + new_we = FALSE; + new_whe = FALSE; + break; + default: + break; + } + + nm_info ("Wireless now %s by radio killswitch", + (new_we && new_whe) ? "enabled" : "disabled"); + if (new_whe != priv->wireless_hw_enabled) { + priv->wireless_hw_enabled = new_whe; + g_object_notify (G_OBJECT (self), NM_MANAGER_WIRELESS_HARDWARE_ENABLED); + } + manager_set_wireless_enabled (self, new_we); +} + +static void +manager_ipw_rfkill_state_changed (NMDeviceWifi *device, + GParamSpec *pspec, + gpointer user_data) +{ + nm_manager_rfkill_update (NM_MANAGER (user_data)); +} + static void add_device (NMManager *self, NMDevice *device) { @@ -1283,15 +1352,25 @@ add_device (NMManager *self, NMDevice *device) G_CALLBACK (manager_device_state_changed), self); - /* Attach to the access-point-added signal so that the manager can fill - * non-SSID-broadcasting APs with an SSID. - */ if (NM_IS_DEVICE_WIFI (device)) { + /* Attach to the access-point-added signal so that the manager can fill + * non-SSID-broadcasting APs with an SSID. + */ g_signal_connect (device, "hidden-ap-found", G_CALLBACK (manager_hidden_ap_found), self); - /* Set initial rfkill state */ + /* Hook up rfkill handling for ipw-based cards until they get converted + * to use the kernel's rfkill subsystem in 2.6.33. + */ + g_signal_connect (device, "notify::" NM_DEVICE_WIFI_IPW_RFKILL_STATE, + G_CALLBACK (manager_ipw_rfkill_state_changed), + self); + + /* Update global rfkill state with this device's rfkill state, and + * then set this device's rfkill state based on the global state. + */ + nm_manager_rfkill_update (self); nm_device_wifi_set_enabled (NM_DEVICE_WIFI (device), priv->wireless_enabled); } @@ -1613,37 +1692,10 @@ udev_device_removed_cb (NMUdevManager *manager, static void udev_manager_rfkill_changed_cb (NMUdevManager *udev_mgr, - RfKillState state, + RfKillState udev_state, gpointer user_data) { - NMManager *self = NM_MANAGER (user_data); - NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); - gboolean new_we = TRUE, new_whe = TRUE; - - switch (state) { - case RFKILL_UNBLOCKED: - new_we = TRUE; - new_whe = TRUE; - break; - case RFKILL_SOFT_BLOCKED: - new_we = FALSE; - new_whe = TRUE; - break; - case RFKILL_HARD_BLOCKED: - new_we = FALSE; - new_whe = FALSE; - break; - default: - break; - } - - nm_info ("Wireless now %s by radio killswitch", - (new_we && new_whe) ? "enabled" : "disabled"); - if (new_whe != priv->wireless_hw_enabled) { - priv->wireless_hw_enabled = new_whe; - g_object_notify (G_OBJECT (self), NM_MANAGER_WIRELESS_HARDWARE_ENABLED); - } - manager_set_wireless_enabled (self, new_we); + nm_manager_rfkill_update (NM_MANAGER (user_data)); } GSList * @@ -2496,6 +2548,11 @@ impl_manager_sleep (NMManager *self, gboolean sleep, GError **error) unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); + /* Ensure rfkill state is up-to-date since we don't respond to state + * changes during sleep. + */ + nm_manager_rfkill_update (self); + /* Re-manage managed devices */ for (iter = priv->devices; iter; iter = iter->next) { NMDevice *device = NM_DEVICE (iter->data); diff --git a/src/nm-rfkill.h b/src/nm-rfkill.h new file mode 100644 index 0000000000..cc03e9e1fa --- /dev/null +++ b/src/nm-rfkill.h @@ -0,0 +1,32 @@ +/* -*- 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) 2007 - 2008 Novell, Inc. + * Copyright (C) 2007 - 2008 Red Hat, Inc. + */ + +#ifndef NM_RFKILL_H +#define NM_RFKILL_H + +typedef enum { + RFKILL_UNBLOCKED = 0, + RFKILL_SOFT_BLOCKED = 1, + RFKILL_HARD_BLOCKED = 2 +} RfKillState; + +#endif /* NM_RFKILL_H */ + diff --git a/src/nm-udev-manager.h b/src/nm-udev-manager.h index 4afcf3b6c7..61b60a40c3 100644 --- a/src/nm-udev-manager.h +++ b/src/nm-udev-manager.h @@ -28,11 +28,7 @@ #define G_UDEV_API_IS_SUBJECT_TO_CHANGE #include -typedef enum { - RFKILL_UNBLOCKED = 0, - RFKILL_SOFT_BLOCKED = 1, - RFKILL_HARD_BLOCKED = 2 -} RfKillState; +#include "nm-rfkill.h" G_BEGIN_DECLS