From: Johannes Berg <johannes@sipsolutions.net>
To: linux-wireless@vger.kernel.org
Subject: [RFT 3/4] cfg80211: add rfkill support
Date: Thu, 21 May 2009 23:59:43 +0200 [thread overview]
Message-ID: <20090521220105.198751775@sipsolutions.net> (raw)
In-Reply-To: 20090521215940.344214804@sipsolutions.net
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@sipsolutions.net>
---
include/net/cfg80211.h | 21 +++++++++--
include/net/mac80211.h | 18 ++++++++--
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 +++---
10 files changed, 145 insertions(+), 29 deletions(-)
--- wireless-testing.orig/net/wireless/core.c 2009-05-21 21:56:48.000000000 +0200
+++ wireless-testing/net/wireless/core.c 2009-05-21 23:45:24.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(-EINVAL);
+ break;
}
- return 0;
+ return NOTIFY_DONE;
}
static struct notifier_block cfg80211_netdev_notifier = {
--- wireless-testing.orig/net/wireless/core.h 2009-05-21 21:56:48.000000000 +0200
+++ wireless-testing/net/wireless/core.h 2009-05-21 22:50:16.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-21 21:56:48.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h 2009-05-21 23:43:53.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;
@@ -1619,4 +1625,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-21 21:56:48.000000000 +0200
+++ wireless-testing/net/wireless/Kconfig 2009-05-21 22:38:31.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-21 22:54:28.000000000 +0200
+++ wireless-testing/net/wireless/wext-compat.c 2009-05-21 23:11:18.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-21 22:57:11.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c 2009-05-21 23:49:58.000000000 +0200
@@ -1339,7 +1339,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:
@@ -1358,14 +1357,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);
@@ -1379,12 +1370,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,
@@ -1426,4 +1421,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-21 22:58:17.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c 2009-05-21 23:18:00.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-21 22:58:36.000000000 +0200
+++ wireless-testing/net/mac80211/util.c 2009-05-21 23:17:24.000000000 +0200
@@ -1092,7 +1092,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-21 22:59:09.000000000 +0200
+++ wireless-testing/include/net/mac80211.h 2009-05-21 23:48:08.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_BEACON_INTERVAL: DEPRECATED
* @IEEE80211_CONF_CHANGE_LISTEN_INTERVAL: the listen interval changed
* @IEEE80211_CONF_CHANGE_RADIOTAP: the radiotap flag changed
@@ -540,7 +540,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_BEACON_INTERVAL = BIT(1),
IEEE80211_CONF_CHANGE_LISTEN_INTERVAL = BIT(2),
IEEE80211_CONF_CHANGE_RADIOTAP = BIT(3),
@@ -559,6 +559,14 @@ __IEEE80211_CONF_CHANGE_BEACON_INTERVAL(
#define IEEE80211_CONF_CHANGE_BEACON_INTERVAL \
__IEEE80211_CONF_CHANGE_BEACON_INTERVAL()
+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
*
@@ -1419,6 +1427,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);
@@ -1467,6 +1479,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-21 23:49:10.000000000 +0200
+++ wireless-testing/net/mac80211/driver-ops.h 2009-05-21 23:49:43.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 */
--
next prev parent reply other threads:[~2009-05-21 22:02 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-05-21 21:59 [RFT 0/4] rfkill improvements Johannes Berg
2009-05-21 21:59 ` [RFT 1/4] net: introduce pre-up netdev notifier Johannes Berg
2009-05-21 21:59 ` [RFT 2/4] rfkill: add function to query state Johannes Berg
2009-05-21 21:59 ` Johannes Berg [this message]
2009-05-23 9:52 ` [RFT 3/4 v2] cfg80211: add rfkill support Johannes Berg
2009-05-24 13:32 ` [RFT 3/4] " Dan Williams
2009-05-24 17:24 ` Johannes Berg
2009-05-26 16:38 ` Marcel Holtmann
2009-05-21 21:59 ` [RFT 4/4] iwlwifi: port to cfg80211 rfkill Johannes Berg
2009-05-23 8:49 ` [RFT 4/4 v2] " Johannes Berg
2009-05-23 8:52 ` [RFT 5/4] iwm: port to new " Johannes Berg
2009-05-25 1:22 ` Zhu Yi
2009-05-25 5:13 ` Johannes Berg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090521220105.198751775@sipsolutions.net \
--to=johannes@sipsolutions.net \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).