summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-06-02 13:01:41 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-06-03 14:06:14 -0400
commit1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3 (patch)
tree642882153a48e910a415e6bb23bcfb79fadef6dd /net
parent6081162e2ed78dfcf149b076b047078ab1445cc2 (diff)
downloadlinux-3.10-1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3.tar.gz
linux-3.10-1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3.tar.bz2
linux-3.10-1f87f7d3a3b42b20f34cb03f0fd1a41c3d0e27f3.zip
cfg80211: add rfkill support
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> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c20
-rw-r--r--net/mac80211/driver-ops.h7
-rw-r--r--net/mac80211/iface.c4
-rw-r--r--net/mac80211/util.c2
-rw-r--r--net/wireless/Kconfig3
-rw-r--r--net/wireless/core.c97
-rw-r--r--net/wireless/core.h7
-rw-r--r--net/wireless/wext-compat.c11
8 files changed, 128 insertions, 23 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 81258acf48b..a9211cc183c 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1340,7 +1340,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
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 wiphy *wiphy,
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 wiphy *wiphy, int *dbm)
*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,
};
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 3912b5334b9..b13446afd48 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -181,4 +181,11 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
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 */
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8c9f1c722cd..b7c8a448429 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -170,7 +170,7 @@ static int ieee80211_open(struct net_device *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_device *dev)
drv_stop(local);
- ieee80211_led_radio(local, 0);
+ ieee80211_led_radio(local, false);
flush_workqueue(local->hw.workqueue);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 31284c984e3..22f63815fb3 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -973,7 +973,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (local->open_count) {
res = drv_start(local);
- ieee80211_led_radio(local, hw->conf.radio_enabled);
+ ieee80211_led_radio(local, true);
}
/* add interfaces */
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 45005497c63..4428dd5e911 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -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"
diff --git a/net/wireless/core.c b/net/wireless/core.c
index a5dbea1da47..3b74b88e10a 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -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_registered_device *rdev,
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;
+}
+
+static 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 cfg80211_ops *ops, int sizeof_priv)
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,10 @@ int wiphy_register(struct wiphy *wiphy)
if (res)
goto out_unlock;
+ res = rfkill_register(drv->rfkill);
+ if (res)
+ goto out_rm_dev;
+
list_add(&drv->list, &cfg80211_drv_list);
/* add to debugfs */
@@ -379,16 +431,41 @@ 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;
}
EXPORT_SYMBOL(wiphy_register);
+void wiphy_rfkill_start_polling(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+ if (!drv->ops->rfkill_poll)
+ return;
+ drv->rfkill_ops.poll = cfg80211_rfkill_poll;
+ rfkill_resume_polling(drv->rfkill);
+}
+EXPORT_SYMBOL(wiphy_rfkill_start_polling);
+
+void wiphy_rfkill_stop_polling(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+
+ rfkill_pause_polling(drv->rfkill);
+}
+EXPORT_SYMBOL(wiphy_rfkill_stop_polling);
+
void wiphy_unregister(struct wiphy *wiphy)
{
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
+ rfkill_unregister(drv->rfkill);
+
/* protect the device list */
mutex_lock(&cfg80211_mutex);
@@ -425,6 +502,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 +516,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 +533,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
struct cfg80211_registered_device *rdev;
if (!dev->ieee80211_ptr)
- return 0;
+ return NOTIFY_DONE;
rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
@@ -492,9 +579,13 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
}
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 = {
diff --git a/net/wireless/core.h b/net/wireless/core.h
index ab512bcd815..bfa340c7abb 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -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
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 9fbfb8536e7..d030c531567 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -764,6 +764,8 @@ int cfg80211_wext_siwtxpower(struct net_device *dev,
/* 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_device *dev,
}
}
} 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_device *dev,
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;