Search Linux Wireless

[PATCH] cfg80211: add rfkill support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



To be easier on drivers and users, have cfg80211 register an
rfkill structure that drivers can access. When soft-killed,
simply take down all interfaces; when hard-killed the driver
needs to notify us and we will take down the interfaces
after the fact. While rfkilled, interfaces cannot be set UP.

Signed-off-by: Johannes Berg <johannes@xxxxxxxxxxxxxxxx>
---
The reason for asking that to be included in .31 is that we really don't
want to have three different rfkill userspace APIs to handle, two are
already enough. If we put this into .32, then we'll have:
 .30 and before: old API
 .31:            /dev/rfkill, but interfaces still up while rfkilled
 .32:            /dev/rfkill, interfaces forced down while rfkilled

This is a bit of a mess, and it would be better to skip the step with
having /dev/rfkill and interfaces up. I'm not sure we can actually do
the driver conversions, but at least we behave better to userspace.

 include/asm-generic/errno.h |    2 +
 include/net/cfg80211.h      |   21 +++++++++--
 include/net/mac80211.h      |   20 +++++++++-
 net/mac80211/cfg.c          |   20 ++++------
 net/mac80211/driver-ops.h   |    7 +++
 net/mac80211/iface.c        |    4 +-
 net/mac80211/util.c         |    2 -
 net/wireless/Kconfig        |    3 +
 net/wireless/core.c         |   81 ++++++++++++++++++++++++++++++++++++++++++--
 net/wireless/core.h         |    7 +++
 net/wireless/wext-compat.c  |   11 +++--
 11 files changed, 148 insertions(+), 30 deletions(-)

--- wireless-testing.orig/net/wireless/core.c	2009-05-29 10:28:02.000000000 +0200
+++ wireless-testing/net/wireless/core.c	2009-05-30 01:38:44.000000000 +0200
@@ -12,6 +12,7 @@
 #include <linux/debugfs.h>
 #include <linux/notifier.h>
 #include <linux/device.h>
+#include <linux/rtnetlink.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
 #include "nl80211.h"
@@ -227,6 +228,41 @@ int cfg80211_dev_rename(struct cfg80211_
 	return 0;
 }
 
+static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
+{
+	struct cfg80211_registered_device *drv = data;
+
+	drv->ops->rfkill_poll(&drv->wiphy);
+}
+
+static int cfg80211_rfkill_set_block(void *data, bool blocked)
+{
+	struct cfg80211_registered_device *drv = data;
+	struct wireless_dev *wdev;
+
+	if (!blocked)
+		return 0;
+
+	rtnl_lock();
+	mutex_lock(&drv->devlist_mtx);
+
+	list_for_each_entry(wdev, &drv->netdev_list, list)
+		dev_close(wdev->netdev);
+
+	mutex_unlock(&drv->devlist_mtx);
+	rtnl_unlock();
+
+	return 0;
+}
+
+void cfg80211_rfkill_sync_work(struct work_struct *work)
+{
+	struct cfg80211_registered_device *drv;
+
+	drv = container_of(work, struct cfg80211_registered_device, rfkill_sync);
+	cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill));
+}
+
 /* exported functions */
 
 struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
@@ -274,6 +310,18 @@ struct wiphy *wiphy_new(const struct cfg
 	drv->wiphy.dev.class = &ieee80211_class;
 	drv->wiphy.dev.platform_data = drv;
 
+	drv->rfkill_ops.set_block = cfg80211_rfkill_set_block;
+	drv->rfkill = rfkill_alloc(dev_name(&drv->wiphy.dev),
+				   &drv->wiphy.dev, RFKILL_TYPE_WLAN,
+				   &drv->rfkill_ops, drv);
+
+	if (!drv->rfkill) {
+		kfree(drv);
+		return NULL;
+	}
+
+	INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work);
+
 	/*
 	 * Initialize wiphy parameters to IEEE 802.11 MIB default values.
 	 * Fragmentation and RTS threshold are disabled by default with the
@@ -356,6 +404,13 @@ int wiphy_register(struct wiphy *wiphy)
 	if (res)
 		goto out_unlock;
 
+	if (wiphy->rfkill_poll && drv->ops->rfkill_poll)
+		drv->rfkill_ops.poll = cfg80211_rfkill_poll;
+
+	res = rfkill_register(drv->rfkill);
+	if (res)
+		goto out_rm_dev;
+
 	list_add(&drv->list, &cfg80211_drv_list);
 
 	/* add to debugfs */
@@ -379,7 +434,11 @@ int wiphy_register(struct wiphy *wiphy)
 	cfg80211_debugfs_drv_add(drv);
 
 	res = 0;
-out_unlock:
+	goto out_unlock;
+
+ out_rm_dev:
+	device_del(&drv->wiphy.dev);
+ out_unlock:
 	mutex_unlock(&cfg80211_mutex);
 	return res;
 }
@@ -389,6 +448,8 @@ void wiphy_unregister(struct wiphy *wiph
 {
 	struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
 
+	rfkill_unregister(drv->rfkill);
+
 	/* protect the device list */
 	mutex_lock(&cfg80211_mutex);
 
@@ -425,6 +486,7 @@ EXPORT_SYMBOL(wiphy_unregister);
 void cfg80211_dev_free(struct cfg80211_registered_device *drv)
 {
 	struct cfg80211_internal_bss *scan, *tmp;
+	rfkill_destroy(drv->rfkill);
 	mutex_destroy(&drv->mtx);
 	mutex_destroy(&drv->devlist_mtx);
 	list_for_each_entry_safe(scan, tmp, &drv->bss_list, list)
@@ -438,6 +500,15 @@ void wiphy_free(struct wiphy *wiphy)
 }
 EXPORT_SYMBOL(wiphy_free);
 
+void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
+{
+	struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+	if (rfkill_set_hw_state(drv->rfkill, blocked))
+		schedule_work(&drv->rfkill_sync);
+}
+EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
+
 static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
 					 unsigned long state,
 					 void *ndev)
@@ -446,7 +517,7 @@ static int cfg80211_netdev_notifier_call
 	struct cfg80211_registered_device *rdev;
 
 	if (!dev->ieee80211_ptr)
-		return 0;
+		return NOTIFY_DONE;
 
 	rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
 
@@ -492,9 +563,13 @@ static int cfg80211_netdev_notifier_call
 		}
 		mutex_unlock(&rdev->devlist_mtx);
 		break;
+	case NETDEV_PRE_UP:
+		if (rfkill_blocked(rdev->rfkill))
+			return notifier_from_errno(-ERFKILL);
+		break;
 	}
 
-	return 0;
+	return NOTIFY_DONE;
 }
 
 static struct notifier_block cfg80211_netdev_notifier = {
--- wireless-testing.orig/net/wireless/core.h	2009-05-30 01:38:27.000000000 +0200
+++ wireless-testing/net/wireless/core.h	2009-05-30 01:38:44.000000000 +0200
@@ -11,6 +11,8 @@
 #include <linux/kref.h>
 #include <linux/rbtree.h>
 #include <linux/debugfs.h>
+#include <linux/rfkill.h>
+#include <linux/workqueue.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
 #include "reg.h"
@@ -24,6 +26,11 @@ struct cfg80211_registered_device {
 	 * any call is in progress */
 	struct mutex mtx;
 
+	/* rfkill support */
+	struct rfkill_ops rfkill_ops;
+	struct rfkill *rfkill;
+	struct work_struct rfkill_sync;
+
 	/* ISO / IEC 3166 alpha2 for which this device is receiving
 	 * country IEs on, this can help disregard country IEs from APs
 	 * on the same alpha2 quickly. The alpha2 may differ from
--- wireless-testing.orig/include/net/cfg80211.h	2009-05-30 01:38:43.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h	2009-05-30 01:38:44.000000000 +0200
@@ -757,13 +757,11 @@ enum wiphy_params_flags {
  * @TX_POWER_AUTOMATIC: the dbm parameter is ignored
  * @TX_POWER_LIMITED: limit TX power by the dbm parameter
  * @TX_POWER_FIXED: fix TX power to the dbm parameter
- * @TX_POWER_OFF: turn off completely (will go away)
  */
 enum tx_power_setting {
 	TX_POWER_AUTOMATIC,
 	TX_POWER_LIMITED,
 	TX_POWER_FIXED,
-	TX_POWER_OFF,
 };
 
 /**
@@ -855,8 +853,10 @@ enum tx_power_setting {
  *
  * @set_tx_power: set the transmit power according to the parameters
  * @get_tx_power: store the current TX power into the dbm variable;
- *	return 0 if successful; or -ENETDOWN if successful but power
- *	is disabled (this will go away)
+ *	return 0 if successful
+ *
+ * @rfkill_poll: polls the hw rfkill line, use cfg80211 reporting
+ *	functions to adjust rfkill hw state
  */
 struct cfg80211_ops {
 	int	(*suspend)(struct wiphy *wiphy);
@@ -952,6 +952,8 @@ struct cfg80211_ops {
 	int	(*set_tx_power)(struct wiphy *wiphy,
 				enum tx_power_setting type, int dbm);
 	int	(*get_tx_power)(struct wiphy *wiphy, int *dbm);
+
+	void	(*rfkill_poll)(struct wiphy *wiphy);
 };
 
 /*
@@ -990,6 +992,8 @@ struct cfg80211_ops {
  * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
  *	-1 = fragmentation disabled, only odd values >= 256 used
  * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled
+ *
+ * @rfkill_poll: poll rfkill via rfkill_poll method, if present
  */
 struct wiphy {
 	/* assign these fields before you register the wiphy */
@@ -1003,6 +1007,8 @@ struct wiphy {
 	bool custom_regulatory;
 	bool strict_regulatory;
 
+	bool rfkill_poll;
+
 	enum cfg80211_signal_type signal_type;
 
 	int bss_priv_size;
@@ -1666,4 +1672,11 @@ void cfg80211_michael_mic_failure(struct
  */
 void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp);
 
+/**
+ * wiphy_rfkill_set_hw_state - notify cfg80211 about hw block state
+ * @wiphy: the wiphy
+ * @blocked: block status
+ */
+void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked);
+
 #endif /* __NET_CFG80211_H */
--- wireless-testing.orig/net/wireless/Kconfig	2009-05-29 10:28:02.000000000 +0200
+++ wireless-testing/net/wireless/Kconfig	2009-05-30 01:38:44.000000000 +0200
@@ -1,5 +1,6 @@
 config CFG80211
-        tristate "Improved wireless configuration API"
+	tristate "Improved wireless configuration API"
+	depends on RFKILL || !RFKILL
 
 config CFG80211_REG_DEBUG
 	bool "cfg80211 regulatory debugging"
--- wireless-testing.orig/net/wireless/wext-compat.c	2009-05-30 01:38:43.000000000 +0200
+++ wireless-testing/net/wireless/wext-compat.c	2009-05-30 01:38:44.000000000 +0200
@@ -764,6 +764,8 @@ int cfg80211_wext_siwtxpower(struct net_
 
 	/* only change when not disabling */
 	if (!data->txpower.disabled) {
+		rfkill_set_sw_state(rdev->rfkill, false);
+
 		if (data->txpower.fixed) {
 			/*
 			 * wext doesn't support negative values, see
@@ -787,7 +789,9 @@ int cfg80211_wext_siwtxpower(struct net_
 			}
 		}
 	} else {
-		type = TX_POWER_OFF;
+		rfkill_set_sw_state(rdev->rfkill, true);
+		schedule_work(&rdev->rfkill_sync);
+		return 0;
 	}
 
 	return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);;
@@ -811,13 +815,12 @@ int cfg80211_wext_giwtxpower(struct net_
 		return -EOPNOTSUPP;
 
 	err = rdev->ops->get_tx_power(wdev->wiphy, &val);
-	/* HACK!!! */
-	if (err && err != -ENETDOWN)
+	if (err)
 		return err;
 
 	/* well... oh well */
 	data->txpower.fixed = 1;
-	data->txpower.disabled = err == -ENETDOWN;
+	data->txpower.disabled = rfkill_blocked(rdev->rfkill);
 	data->txpower.value = val;
 	data->txpower.flags = IW_TXPOW_DBM;
 
--- wireless-testing.orig/net/mac80211/cfg.c	2009-05-30 01:38:43.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c	2009-05-30 01:38:44.000000000 +0200
@@ -1340,7 +1340,6 @@ static int ieee80211_set_tx_power(struct
 	struct ieee80211_local *local = wiphy_priv(wiphy);
 	struct ieee80211_channel *chan = local->hw.conf.channel;
 	u32 changes = 0;
-	bool radio_enabled = true;
 
 	switch (type) {
 	case TX_POWER_AUTOMATIC:
@@ -1359,14 +1358,6 @@ static int ieee80211_set_tx_power(struct
 			return -EINVAL;
 		local->user_power_level = dbm;
 		break;
-	case TX_POWER_OFF:
-		radio_enabled = false;
-		break;
-	}
-
-	if (radio_enabled != local->hw.conf.radio_enabled) {
-		changes |= IEEE80211_CONF_CHANGE_RADIO_ENABLED;
-		local->hw.conf.radio_enabled = radio_enabled;
 	}
 
 	ieee80211_hw_config(local, changes);
@@ -1380,12 +1371,16 @@ static int ieee80211_get_tx_power(struct
 
 	*dbm = local->hw.conf.power_level;
 
-	if (!local->hw.conf.radio_enabled)
-		return -ENETDOWN;
-
 	return 0;
 }
 
+static void ieee80211_rfkill_poll(struct wiphy *wiphy)
+{
+	struct ieee80211_local *local = wiphy_priv(wiphy);
+
+	drv_rfkill_poll(local);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
 	.add_virtual_intf = ieee80211_add_iface,
 	.del_virtual_intf = ieee80211_del_iface,
@@ -1427,4 +1422,5 @@ struct cfg80211_ops mac80211_config_ops 
 	.set_wiphy_params = ieee80211_set_wiphy_params,
 	.set_tx_power = ieee80211_set_tx_power,
 	.get_tx_power = ieee80211_get_tx_power,
+	.rfkill_poll = ieee80211_rfkill_poll,
 };
--- wireless-testing.orig/net/mac80211/iface.c	2009-05-29 10:28:02.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c	2009-05-30 01:38:44.000000000 +0200
@@ -170,7 +170,7 @@ static int ieee80211_open(struct net_dev
 			goto err_del_bss;
 		/* we're brought up, everything changes */
 		hw_reconf_flags = ~0;
-		ieee80211_led_radio(local, local->hw.conf.radio_enabled);
+		ieee80211_led_radio(local, true);
 	}
 
 	/*
@@ -560,7 +560,7 @@ static int ieee80211_stop(struct net_dev
 
 		drv_stop(local);
 
-		ieee80211_led_radio(local, 0);
+		ieee80211_led_radio(local, false);
 
 		flush_workqueue(local->hw.workqueue);
 
--- wireless-testing.orig/net/mac80211/util.c	2009-05-30 01:38:42.000000000 +0200
+++ wireless-testing/net/mac80211/util.c	2009-05-30 01:38:44.000000000 +0200
@@ -1019,7 +1019,7 @@ int ieee80211_reconfig(struct ieee80211_
 	if (local->open_count) {
 		res = drv_start(local);
 
-		ieee80211_led_radio(local, hw->conf.radio_enabled);
+		ieee80211_led_radio(local, true);
 	}
 
 	/* add interfaces */
--- wireless-testing.orig/include/net/mac80211.h	2009-05-30 01:38:43.000000000 +0200
+++ wireless-testing/include/net/mac80211.h	2009-05-30 01:38:44.000000000 +0200
@@ -529,7 +529,7 @@ enum ieee80211_conf_flags {
 /**
  * enum ieee80211_conf_changed - denotes which configuration changed
  *
- * @IEEE80211_CONF_CHANGE_RADIO_ENABLED: the value of radio_enabled changed
+ * @_IEEE80211_CONF_CHANGE_RADIO_ENABLED: DEPRECATED
  * @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed
  * @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed
  * @IEEE80211_CONF_CHANGE_PS: the PS flag or dynamic PS timeout changed
@@ -539,7 +539,7 @@ enum ieee80211_conf_flags {
  * @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
  */
 enum ieee80211_conf_changed {
-	IEEE80211_CONF_CHANGE_RADIO_ENABLED	= BIT(0),
+	_IEEE80211_CONF_CHANGE_RADIO_ENABLED	= BIT(0),
 	IEEE80211_CONF_CHANGE_LISTEN_INTERVAL	= BIT(2),
 	IEEE80211_CONF_CHANGE_RADIOTAP		= BIT(3),
 	IEEE80211_CONF_CHANGE_PS		= BIT(4),
@@ -549,6 +549,14 @@ enum ieee80211_conf_changed {
 	IEEE80211_CONF_CHANGE_IDLE		= BIT(8),
 };
 
+static inline __deprecated enum ieee80211_conf_changed
+__IEEE80211_CONF_CHANGE_RADIO_ENABLED(void)
+{
+	return _IEEE80211_CONF_CHANGE_RADIO_ENABLED;
+}
+#define IEEE80211_CONF_CHANGE_RADIO_ENABLED \
+	__IEEE80211_CONF_CHANGE_RADIO_ENABLED()
+
 /**
  * struct ieee80211_conf - configuration of the device
  *
@@ -588,7 +596,7 @@ struct ieee80211_conf {
 	int max_sleep_period;
 
 	u16 listen_interval;
-	bool radio_enabled;
+	bool __deprecated radio_enabled;
 
 	u8 long_frame_max_tx_count, short_frame_max_tx_count;
 
@@ -1399,6 +1407,10 @@ enum ieee80211_ampdu_mlme_action {
  * 	is the first frame we expect to perform the action on. Notice
  * 	that TX/RX_STOP can pass NULL for this parameter.
  *	Returns a negative error code on failure.
+ *
+ * @rfkill_poll: Poll rfkill hardware state. If you need this, you also
+ *	need to set wiphy->rfkill_poll to %true before registration,
+ *	and need to call wiphy_rfkill_set_hw_state() in the callback.
  */
 struct ieee80211_ops {
 	int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
@@ -1447,6 +1459,8 @@ struct ieee80211_ops {
 	int (*ampdu_action)(struct ieee80211_hw *hw,
 			    enum ieee80211_ampdu_mlme_action action,
 			    struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+
+	void (*rfkill_poll)(struct ieee80211_hw *hw);
 };
 
 /**
--- wireless-testing.orig/net/mac80211/driver-ops.h	2009-05-29 10:28:02.000000000 +0200
+++ wireless-testing/net/mac80211/driver-ops.h	2009-05-30 01:38:44.000000000 +0200
@@ -181,4 +181,11 @@ static inline int drv_ampdu_action(struc
 						sta, tid, ssn);
 	return -EOPNOTSUPP;
 }
+
+
+static inline void drv_rfkill_poll(struct ieee80211_local *local)
+{
+	if (local->ops->rfkill_poll)
+		local->ops->rfkill_poll(&local->hw);
+}
 #endif /* __MAC80211_DRIVER_OPS */
--- wireless-testing.orig/include/asm-generic/errno.h	2009-05-29 10:28:02.000000000 +0200
+++ wireless-testing/include/asm-generic/errno.h	2009-05-30 01:38:44.000000000 +0200
@@ -106,4 +106,6 @@
 #define	EOWNERDEAD	130	/* Owner died */
 #define	ENOTRECOVERABLE	131	/* State not recoverable */
 
+#define ERFKILL		132	/* Operation not possible due to RF-kill */
+
 #endif


--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux