From e123fcb14305084d2e9da9d19eed6999769475ed Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 24 Nov 2009 10:43:43 -0800 Subject: [PATCH] wifi: poll rfkill status for ipw2x00 devices The ipw2x00 drivers won't be converted over to the kernel's rfkill subsystem until 2.6.33, and thus listening for udev rfkill change events on these devices doesn't work. So until then, poll rfkill state for ipw2x00 devices every few seconds in addition to listening to other rfkill sources. --- src/nm-device-wifi.c | 111 ++++++++++++++++++++++++++++++++++- src/nm-device-wifi.h | 5 +- src/nm-manager.c | 133 ++++++++++++++++++++++++++++++------------ src/nm-rfkill.h | 32 ++++++++++ src/nm-udev-manager.h | 6 +- 5 files changed, 240 insertions(+), 47 deletions(-) create mode 100644 src/nm-rfkill.h 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