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.
This commit is contained in:
Dan Williams 2009-11-24 10:43:43 -08:00
parent 66994a18a3
commit e123fcb143
5 changed files with 240 additions and 47 deletions

View file

@ -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",

View file

@ -26,7 +26,7 @@
#include <dbus/dbus.h>
#include <net/ethernet.h>
#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 */

View file

@ -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);

32
src/nm-rfkill.h Normal file
View file

@ -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 */

View file

@ -28,11 +28,7 @@
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
#include <gudev/gudev.h>
typedef enum {
RFKILL_UNBLOCKED = 0,
RFKILL_SOFT_BLOCKED = 1,
RFKILL_HARD_BLOCKED = 2
} RfKillState;
#include "nm-rfkill.h"
G_BEGIN_DECLS