Skip to content

Commit

Permalink
mac80211: add basic support for WoWLAN
Browse files Browse the repository at this point in the history
This adds basic support for the new WoWLAN
configuration in mac80211. The behaviour is
completely offloaded to the driver though,
with two new callbacks (suspend/resume).

Options for the driver include a complete
reconfiguration after wakeup, and exposing
all the triggers it wants to support.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Johannes Berg authored and John W. Linville committed May 5, 2011
1 parent ff1b6e6 commit eecc480
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 5 deletions.
16 changes: 16 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,18 @@ enum ieee80211_ampdu_mlme_action {
* you should ensure to cancel it on this callback.
* Must be implemented and can sleep.
*
* @suspend: Suspend the device; mac80211 itself will quiesce before and
* stop transmitting and doing any other configuration, and then
* ask the device to suspend. This is only invoked when WoWLAN is
* configured, otherwise the device is deconfigured completely and
* reconfigured at resume time.
*
* @resume: If WoWLAN was configured, this indicates that mac80211 is
* now resuming its operation, after this the device must be fully
* functional again. If this returns an error, the only way out is
* to also unregister the device. If it returns 1, then mac80211
* will also go through the regular complete restart on resume.
*
* @add_interface: Called when a netdevice attached to the hardware is
* enabled. Because it is not called for monitor mode devices, @start
* and @stop must be implemented.
Expand Down Expand Up @@ -1831,6 +1843,10 @@ struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
int (*start)(struct ieee80211_hw *hw);
void (*stop)(struct ieee80211_hw *hw);
#ifdef CONFIG_PM
int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
int (*resume)(struct ieee80211_hw *hw);
#endif
int (*add_interface)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
int (*change_interface)(struct ieee80211_hw *hw,
Expand Down
2 changes: 1 addition & 1 deletion net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
static int ieee80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wowlan)
{
return __ieee80211_suspend(wiphy_priv(wiphy));
return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
}

static int ieee80211_resume(struct wiphy *wiphy)
Expand Down
2 changes: 1 addition & 1 deletion net/mac80211/debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
struct ieee80211_local *local = file->private_data;

rtnl_lock();
__ieee80211_suspend(&local->hw);
__ieee80211_suspend(&local->hw, NULL);
__ieee80211_resume(&local->hw);
rtnl_unlock();

Expand Down
27 changes: 27 additions & 0 deletions net/mac80211/driver-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,33 @@ static inline void drv_stop(struct ieee80211_local *local)
local->started = false;
}

#ifdef CONFIG_PM
static inline int drv_suspend(struct ieee80211_local *local,
struct cfg80211_wowlan *wowlan)
{
int ret;

might_sleep();

trace_drv_suspend(local);
ret = local->ops->suspend(&local->hw, wowlan);
trace_drv_return_int(local, ret);
return ret;
}

static inline int drv_resume(struct ieee80211_local *local)
{
int ret;

might_sleep();

trace_drv_resume(local);
ret = local->ops->resume(&local->hw);
trace_drv_return_int(local, ret);
return ret;
}
#endif

static inline int drv_add_interface(struct ieee80211_local *local,
struct ieee80211_vif *vif)
{
Expand Down
10 changes: 10 additions & 0 deletions net/mac80211/driver-trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ DEFINE_EVENT(local_only_evt, drv_start,
TP_ARGS(local)
);

DEFINE_EVENT(local_only_evt, drv_suspend,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
);

DEFINE_EVENT(local_only_evt, drv_resume,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
);

DEFINE_EVENT(local_only_evt, drv_stop,
TP_PROTO(struct ieee80211_local *local),
TP_ARGS(local)
Expand Down
9 changes: 7 additions & 2 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,9 @@ struct ieee80211_local {
/* device is started */
bool started;

/* wowlan is enabled -- don't reconfig on resume */
bool wowlan;

int tx_headroom; /* required headroom for hardware/radiotap */

/* count for keys needing tailroom space allocation */
Expand Down Expand Up @@ -1250,7 +1253,8 @@ int ieee80211_reconfig(struct ieee80211_local *local);
void ieee80211_stop_device(struct ieee80211_local *local);

#ifdef CONFIG_PM
int __ieee80211_suspend(struct ieee80211_hw *hw);
int __ieee80211_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan);

static inline int __ieee80211_resume(struct ieee80211_hw *hw)
{
Expand All @@ -1263,7 +1267,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
return ieee80211_reconfig(hw_to_local(hw));
}
#else
static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
return 0;
}
Expand Down
4 changes: 4 additions & 0 deletions net/mac80211/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};

if ((hw->wiphy->wowlan.flags || hw->wiphy->wowlan.n_patterns) &&
(!local->ops->suspend || !local->ops->resume))
return -EINVAL;

if (hw->max_report_rates == 0)
hw->max_report_rates = hw->max_rates;

Expand Down
13 changes: 12 additions & 1 deletion net/mac80211/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "driver-ops.h"
#include "led.h"

int __ieee80211_suspend(struct ieee80211_hw *hw)
int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
Expand Down Expand Up @@ -47,6 +47,16 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
cancel_work_sync(&local->dynamic_ps_enable_work);
del_timer_sync(&local->dynamic_ps_timer);

local->wowlan = wowlan && local->open_count;
if (local->wowlan) {
int err = drv_suspend(local, wowlan);
if (err) {
local->quiescing = false;
return err;
}
goto suspend;
}

/* disable keys */
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_disable_keys(sdata);
Expand Down Expand Up @@ -104,6 +114,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
if (local->open_count)
ieee80211_stop_device(local);

suspend:
local->suspended = true;
/* need suspended to be visible before quiescing is false */
barrier();
Expand Down
19 changes: 19 additions & 0 deletions net/mac80211/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1125,9 +1125,27 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta;
int res;

#ifdef CONFIG_PM
if (local->suspended)
local->resuming = true;

if (local->wowlan) {
local->wowlan = false;
res = drv_resume(local);
if (res < 0) {
local->resuming = false;
return res;
}
if (res == 0)
goto wake_up;
WARN_ON(res > 1);
/*
* res is 1, which means the driver requested
* to go through a regular reset on wakeup.
*/
}
#endif

/* restart hardware */
if (local->open_count) {
/*
Expand Down Expand Up @@ -1258,6 +1276,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);

wake_up:
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);

Expand Down

0 comments on commit eecc480

Please sign in to comment.