Skip to content

Commit

Permalink
cfg80211: add rfkill support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jmberg authored and linvjw committed Jun 3, 2009
1 parent 6081162 commit 1f87f7d
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 30 deletions.
2 changes: 2 additions & 0 deletions include/asm-generic/errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
29 changes: 25 additions & 4 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

/**
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
};

/*
Expand Down Expand Up @@ -1666,4 +1668,23 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
*/
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);

/**
* wiphy_rfkill_start_polling - start polling rfkill
* @wiphy: the wiphy
*/
void wiphy_rfkill_start_polling(struct wiphy *wiphy);

/**
* wiphy_rfkill_stop_polling - stop polling rfkill
* @wiphy: the wiphy
*/
void wiphy_rfkill_stop_polling(struct wiphy *wiphy);

#endif /* __NET_CFG80211_H */
20 changes: 17 additions & 3 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,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
Expand All @@ -536,7 +536,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),
Expand All @@ -546,6 +546,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
*
Expand Down Expand Up @@ -585,7 +593,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;

Expand Down Expand Up @@ -1396,6 +1404,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);
Expand Down Expand Up @@ -1444,6 +1456,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);
};

/**
Expand Down
20 changes: 8 additions & 12 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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,
};
7 changes: 7 additions & 0 deletions net/mac80211/driver-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
4 changes: 2 additions & 2 deletions net/mac80211/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/*
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion net/mac80211/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
3 changes: 2 additions & 1 deletion net/wireless/Kconfig
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
97 changes: 94 additions & 3 deletions net/wireless/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand All @@ -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);

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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);

Expand Down Expand Up @@ -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 = {
Expand Down
Loading

0 comments on commit 1f87f7d

Please sign in to comment.