2009-05-13 09:21:00

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC v2 0/5] WoW suport

Here's take II, this time with some cfg80211 and minimal mac80211 integration.
I'm not too sure on the parts needed on mac80211 yet so could use some help there.

Unfortunately it doesn't work yet :( but then again I haven't yet tested this
on another device. I'll do so eventually.

Luis R. Rodriguez (5):
mac80211: fix idle trigger upon resume
mac80211: inform devices when we are suspending on the stop callback
cfg80211: add WoW support
mac80211: add WoW support
ath9k: Add Wake-on-Wireless-LAN support

drivers/net/wireless/adm8211.c | 2 +-
drivers/net/wireless/at76c50x-usb.c | 2 +-
drivers/net/wireless/ath/ar9170/main.c | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 +-
drivers/net/wireless/ath/ath9k/Makefile | 1 +
drivers/net/wireless/ath/ath9k/ath9k.h | 17 +
drivers/net/wireless/ath/ath9k/debug.c | 1 -
drivers/net/wireless/ath/ath9k/debug.h | 1 +
drivers/net/wireless/ath/ath9k/hw.c | 22 ++
drivers/net/wireless/ath/ath9k/hw.h | 21 +
drivers/net/wireless/ath/ath9k/initvals.h | 31 ++
drivers/net/wireless/ath/ath9k/main.c | 23 ++-
drivers/net/wireless/ath/ath9k/pci.c | 105 +++++
drivers/net/wireless/ath/ath9k/reg.h | 154 ++++++++
drivers/net/wireless/ath/ath9k/wow.c | 543 +++++++++++++++++++++++++++
drivers/net/wireless/b43/main.c | 2 +-
drivers/net/wireless/b43legacy/main.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +-
drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +-
drivers/net/wireless/libertas_tf/main.c | 2 +-
drivers/net/wireless/mac80211_hwsim.c | 2 +-
drivers/net/wireless/mwl8k.c | 2 +-
drivers/net/wireless/p54/p54common.c | 2 +-
drivers/net/wireless/rt2x00/rt2x00.h | 2 +-
drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +-
drivers/net/wireless/rtl818x/rtl8180_dev.c | 2 +-
drivers/net/wireless/rtl818x/rtl8187_dev.c | 2 +-
drivers/net/wireless/wl12xx/main.c | 2 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 2 +-
include/linux/nl80211.h | 37 ++
include/net/cfg80211.h | 28 ++
include/net/mac80211.h | 15 +-
net/mac80211/cfg.c | 11 +
net/mac80211/driver-ops.h | 2 +-
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/iface.c | 4 +
net/mac80211/pm.c | 8 +
net/mac80211/util.c | 8 +
net/wireless/nl80211.c | 121 ++++++
39 files changed, 1166 insertions(+), 27 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath9k/wow.c



2009-05-13 17:27:52

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 3/5] cfg80211: add WoW support

On Wed, May 13, 2009 at 2:58 AM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
>
>> =C2=A0/**
>> + * struct cfg80211_wow - Wake on Wireless-LAN support info
>> + *
>> + * This structure defines the WoW triggers supported by the device
>> + * and also what specific WoW triggers the user wants enabled. To u=
se
>> + * WoW you can use standard ethernet utilities as you would with
>> + * Wake-on-LAN.
>> + *
>> + * @triggers_supported: supported bitmask of WoW triggers.
>> + * =C2=A0 The flags for this bitmask are %NL80211_WOW_TRIGGER_*.
>> + * @triggers_enabled: enabled triggers by the user. Default
>> + * =C2=A0 is to disable all triggers. The flags for this bitmask
>> + * =C2=A0 are %NL80211_WOW_TRIGGER_*.
>> + */
>> +struct cfg80211_wow {
>> + =C2=A0 =C2=A0 u32 triggers_supported;
>> + =C2=A0 =C2=A0 u32 triggers_enabled;
>> +};
>> +
>> +/**
>> =C2=A0 * struct cfg80211_ops - backend description for wireless conf=
iguration
>> =C2=A0 *
>> =C2=A0 * This struct is registered by fullmac card drivers and/or wi=
reless stacks
>> @@ -849,6 +868,8 @@ enum wiphy_params_flags {
>> =C2=A0 * =C2=A0 @changed bitfield (see &enum wiphy_params_flags) des=
cribes which values
>> =C2=A0 * =C2=A0 have changed. The actual parameter values are availa=
ble in
>> =C2=A0 * =C2=A0 struct wiphy. If returning an error, no value should=
be changed.
>> + *
>> + * @set_wow: used to inform the device which WoW triggers should be=
enabled.
>> =C2=A0 */
>> =C2=A0struct cfg80211_ops {
>> =C2=A0 =C2=A0 =C2=A0 int =C2=A0 =C2=A0 (*suspend)(struct wiphy *wiph=
y);
>> @@ -940,6 +961,7 @@ struct cfg80211_ops {
>> =C2=A0 =C2=A0 =C2=A0 int =C2=A0 =C2=A0 (*leave_ibss)(struct wiphy *w=
iphy, struct net_device *dev);
>>
>> =C2=A0 =C2=A0 =C2=A0 int =C2=A0 =C2=A0 (*set_wiphy_params)(struct wi=
phy *wiphy, u32 changed);
>> + =C2=A0 =C2=A0 int =C2=A0 =C2=A0 (*set_wow)(struct wiphy *wiphy, u3=
2 triggers);
>> =C2=A0};
>>
>> =C2=A0/*
>> @@ -965,6 +987,11 @@ struct cfg80211_ops {
>> =C2=A0 * =C2=A0 channels at a later time. This can be used for devic=
es which do not
>> =C2=A0 * =C2=A0 have calibration information gauranteed for frequenc=
ies or settings
>> =C2=A0 * =C2=A0 outside of its regulatory domain.
>> + * @wow: Wake-on-Wireless-LAN configuration struct. The driver shou=
ld
>> + * =C2=A0 set the capabilities before registering the wiphy. When w=
e
>> + * =C2=A0 get a request to enable certain WoW events we will inform
>> + * =C2=A0 the driver through the set_wow() callback. If this is suc=
cessfull
>> + * =C2=A0 we then set the passed triggers as enabled on the wiphy'w=
wow struct.
>
> I'm not sure that makes sense. Why keep track of both the enabled and
> the possible triggers, _and_ have a set_wow callback? The latter migh=
t
> make sense to verify some of the information, but I think we shouldn'=
t
> keep track of the enabled triggers here but rather do that in the dri=
ver
> then.
>
> Alternatively, how about we push the stuff I suggested for mac80211 a=
ll
> the way through to cfg80211? The cfg80211 suspend() method could pass
> through the wow parameters.

Sure, makes sense.

Luis

2009-05-13 23:25:37

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 4:13 PM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2009-05-13 at 16:08 -0700, Luis R. Rodriguez wrote:

>> > =C2=A0* maybe not remove all the sta info structs
>>
>> Which ones? In my tests I'm just not removing any.
>
> I'm talking about sta_notify() not code :)

Oh I wasn't talking about code either but about early exist stuff I
was doing upon __ieee80211_suspend().

>> > =C2=A0* and whatever else may be necessary -- that might even depe=
nd on the
>> > =C2=A0 wow mode
>>
>> So lets start with a basic goal -- magic packet. If you think about =
it
>> though if you disassociate you won't get this magic packet
>
> Actually I think you still can or something, it's very strange. Does
> anyone have a WoW documentation/spec?

Good question. I'll poke internally see what I can get.

>> so I guess
>> that's also why we have link-change trigger (or called disassoc
>> trigger on some other hardware?).
>
> That could make some sense, though do you want to wake up, reassociat=
e
> (if possible) and go to sleep again? That would take some
> synchronisation across layers...

I hope not, but anyway if that is done someone can use custom scripts
to go back to sleep, or do we even want to bother passing this up to
the supplicant as an event upon resume? Hmm -- yeah Jouni, would the
supplicant benefit from having WoW events sent back to it after
resume, or even before suspend?

>> =C2=A0Anyway out of these link change and
>> magic packet seem like a reasonable goal to get working first. For
>> that besides the above I'm not sure what else is required.
>>
>> I'm building this stuff now on a box with a card that is supposed to
>> have WoW working so I'll know for sure if we are missing something
>> soon.
>
> Cool.
>
> Anyway I think Bob is right, we may not need to have anything added t=
o
> ->stop() and instead do some wow config, or something. Too tired to
> think about it I think.

I added a WOW hw config, if we use that then we need a way for the
device to get the wow config stuff. I was thinking we'd use the
wiphy->wow for that but it seems you rather we just pass the settings
completely up once and let the driver worry how to stuff this in the
driver somewhere.

Luis

2009-05-13 09:55:46

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 3/5] cfg80211: add WoW support

On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:

> +/**
> + * enum nl80211_wow_triggers - Wake-on-Wireless-LAN triggers
> + *
> + * NL80211_WOW_TRIGGER_MAGIC_PACKET: a wake signal will be sent to the
> + * devices if a magic packet is received.
> + * NL80211_WOW_TRIGGER_BMISS: WoW signal will be sent to the device when
> + * a beacon has been missed by the associated AP.
> + * NL80211_WOW_TRIGGER_LINK_CHANGE: a wake signal will be sent to
> + * the device if a link change is detected on the device.
> + * NL80211_WOW_TRIGGER_USER_PATTERN: a wake sigal will be sent to the
> + * device if a user configurable pattern is received by
> + * the device.
> + */
> +enum nl80211_wow_triggers {
> + NL80211_WOW_TRIGGER_MAGIC_PACKET = 1 << 0,
> + NL80211_WOW_TRIGGER_BMISS = 1 << 1,
> + NL80211_WOW_TRIGGER_LINK_CHANGE = 1 << 2,
> + NL80211_WOW_TRIGGER_PATTERN = 1 << 3,
> +};

Seems like you need to have a way to set the pattern and magic packet?

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-05-13 09:21:00

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC v2 1/5] mac80211: fix idle trigger upon resume

When we suspend we stop the queues, then upon resume
the new idle checks will immediately pick up we're
idle and call our driver's config callback. At
this point its pointless to to call this callback
as we haven't yet come back from suspend. We avoid
then making assumptions about being idle until we know
we've come back from suspend.

Signed-off-by: Luis R. Rodriguez <[email protected]>
---
net/mac80211/ieee80211_i.h | 2 ++
net/mac80211/iface.c | 4 ++++
net/mac80211/pm.c | 2 ++
3 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index fec3ded..43f3d07 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -609,6 +609,7 @@ struct ieee80211_local {
unsigned int filter_flags; /* FIF_* */
struct iw_statistics wstats;
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
+ bool suspended; /* true if we finished suspend work for it */
int tx_headroom; /* required headroom for hardware/radiotap */

/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -1047,6 +1048,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw);

static inline int __ieee80211_resume(struct ieee80211_hw *hw)
{
+ hw_to_local(hw)->suspended = false;
return ieee80211_reconfig(hw_to_local(hw));
}
#else
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8c9f1c7..5e091a7 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -931,6 +931,10 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
int count = 0;

+ /* allow resume to finish before we make any assumptions */
+ if (local->suspended)
+ return 0;
+
if (local->hw_scanning || local->sw_scanning)
return ieee80211_idle_off(local, "scanning");

diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 9d3d89a..8883d05 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -65,6 +65,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
/* flush again, in case driver queued work */
flush_workqueue(local->hw.workqueue);

+ local->suspended = true;
+
/* stop hardware */
if (local->open_count) {
ieee80211_led_radio(local, false);
--
1.6.0.6


2009-05-13 21:09:38

by Bob Copeland

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 2:13 PM, Luis R. Rodriguez
<[email protected]> wrote:
> Agreed, however ath9k is the only card I know how WoW works so far.

Understood.

> Thoughts?

None really, it just seemed adding flags to stop() is a less-than-ideal
interface. Hmm, maybe there is inspiration to be had from plain old
ethernet drivers...

<looks quickly>

The one example I looked at, amd8111e, used set_wol/get_wol callbacks
from ethtool to set the option, then did something special in its bus
suspend method for the magic. *shrug*

--
Bob Copeland %% http://www.bobcopeland.com

2009-05-13 23:14:18

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, 2009-05-13 at 16:08 -0700, Luis R. Rodriguez wrote:

> > That's a good point. Somehow I thought this was intimately tied to
> > suspend. And I think for cfg80211 that makes sense, since cfg80211
> > doesn't do much for suspend. But for mac80211 you're right in that it
> > would make sense to
> > * keep the radio on
>
> This is implicit by not calling stop, right?

Yeah, I think so.

> > * not call stop
>
> This makes sense for ath9k so far -- I was just exiting out.

Ok.

> > * maybe not remove all the sta info structs
>
> Which ones? In my tests I'm just not removing any.

I'm talking about sta_notify() not code :)

> > * and whatever else may be necessary -- that might even depend on the
> > wow mode
>
> So lets start with a basic goal -- magic packet. If you think about it
> though if you disassociate you won't get this magic packet

Actually I think you still can or something, it's very strange. Does
anyone have a WoW documentation/spec?

> so I guess
> that's also why we have link-change trigger (or called disassoc
> trigger on some other hardware?).

That could make some sense, though do you want to wake up, reassociate
(if possible) and go to sleep again? That would take some
synchronisation across layers...

> Anyway out of these link change and
> magic packet seem like a reasonable goal to get working first. For
> that besides the above I'm not sure what else is required.
>
> I'm building this stuff now on a box with a card that is supposed to
> have WoW working so I'll know for sure if we are missing something
> soon.

Cool.

Anyway I think Bob is right, we may not need to have anything added to
->stop() and instead do some wow config, or something. Too tired to
think about it I think.

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-05-13 09:21:00

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

We inform the drivers so they can take appropriate actions during
suspend for WoW support.

Signed-off-by: Luis R. Rodriguez <[email protected]>
---
drivers/net/wireless/adm8211.c | 2 +-
drivers/net/wireless/at76c50x-usb.c | 2 +-
drivers/net/wireless/ath/ar9170/main.c | 2 +-
drivers/net/wireless/ath/ath5k/base.c | 4 ++--
drivers/net/wireless/ath/ath9k/main.c | 2 +-
drivers/net/wireless/b43/main.c | 2 +-
drivers/net/wireless/b43legacy/main.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +-
drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +-
drivers/net/wireless/libertas_tf/main.c | 2 +-
drivers/net/wireless/mac80211_hwsim.c | 2 +-
drivers/net/wireless/mwl8k.c | 2 +-
drivers/net/wireless/p54/p54common.c | 2 +-
drivers/net/wireless/rt2x00/rt2x00.h | 2 +-
drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +-
drivers/net/wireless/rtl818x/rtl8180_dev.c | 2 +-
drivers/net/wireless/rtl818x/rtl8187_dev.c | 2 +-
drivers/net/wireless/wl12xx/main.c | 2 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 2 +-
include/net/mac80211.h | 12 +++++++-----
net/mac80211/driver-ops.h | 2 +-
21 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index 316df03..bfcf9ce 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1546,7 +1546,7 @@ fail:
return retval;
}

-static void adm8211_stop(struct ieee80211_hw *dev)
+static void adm8211_stop(struct ieee80211_hw *dev, bool suspend)
{
struct adm8211_priv *priv = dev->priv;

diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index e3caeef..e9ae9f7 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1766,7 +1766,7 @@ error:
return 0;
}

-static void at76_mac80211_stop(struct ieee80211_hw *hw)
+static void at76_mac80211_stop(struct ieee80211_hw *hw, bool suspend)
{
struct at76_priv *priv = hw->priv;

diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c
index 4ef1d2f..f85c981 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1003,7 +1003,7 @@ out:
return err;
}

-static void ar9170_op_stop(struct ieee80211_hw *hw)
+static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct ar9170 *ar = hw->priv;

diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index dbfe9f4..6a917ab 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -221,7 +221,7 @@ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan);
static int ath5k_reset_wake(struct ath5k_softc *sc);
static int ath5k_start(struct ieee80211_hw *hw);
-static void ath5k_stop(struct ieee80211_hw *hw);
+static void ath5k_stop(struct ieee80211_hw *hw, bool suspend);
static int ath5k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
static void ath5k_remove_interface(struct ieee80211_hw *hw,
@@ -2700,7 +2700,7 @@ static int ath5k_start(struct ieee80211_hw *hw)
return ath5k_init(hw->priv);
}

-static void ath5k_stop(struct ieee80211_hw *hw)
+static void ath5k_stop(struct ieee80211_hw *hw, bool suspend)
{
ath5k_stop_hw(hw->priv);
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 7a1a8d3..fa1acf1 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2118,7 +2118,7 @@ exit:
return 0;
}

-static void ath9k_stop(struct ieee80211_hw *hw)
+static void ath9k_stop(struct ieee80211_hw *hw, bool suspend)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 2615aaf..177db73 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4348,7 +4348,7 @@ static int b43_op_start(struct ieee80211_hw *hw)
return err;
}

-static void b43_op_stop(struct ieee80211_hw *hw)
+static void b43_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
struct b43_wldev *dev = wl->current_dev;
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 07c7898..7bac6be 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -3514,7 +3514,7 @@ out_mutex_unlock:
return err;
}

-static void b43legacy_op_stop(struct ieee80211_hw *hw)
+static void b43legacy_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
struct b43legacy_wldev *dev = wl->current_dev;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 596977d..e48c345 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1985,7 +1985,7 @@ out:
return 0;
}

-static void iwl_mac_stop(struct ieee80211_hw *hw)
+static void iwl_mac_stop(struct ieee80211_hw *hw, bool suspend)
{
struct iwl_priv *priv = hw->priv;

diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index c32ec80..01b428d 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3397,7 +3397,7 @@ out_release_irq:
return ret;
}

-static void iwl3945_mac_stop(struct ieee80211_hw *hw)
+static void iwl3945_mac_stop(struct ieee80211_hw *hw, bool suspend)
{
struct iwl_priv *priv = hw->priv;

diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c
index 10a99e2..07b2620 100644
--- a/drivers/net/wireless/libertas_tf/main.c
+++ b/drivers/net/wireless/libertas_tf/main.c
@@ -291,7 +291,7 @@ err_prog_firmware:
return ret;
}

-static void lbtf_op_stop(struct ieee80211_hw *hw)
+static void lbtf_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct lbtf_private *priv = hw->priv;
unsigned long flags;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 61a4ad7..388068b 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -477,7 +477,7 @@ static int mac80211_hwsim_start(struct ieee80211_hw *hw)
}


-static void mac80211_hwsim_stop(struct ieee80211_hw *hw)
+static void mac80211_hwsim_stop(struct ieee80211_hw *hw, bool suspend)
{
struct mac80211_hwsim_data *data = hw->priv;
data->started = 0;
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index a263d5c..97f1c99 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -2916,7 +2916,7 @@ static int mwl8k_stop_wt(struct work_struct *wt)
return rc;
}

-static void mwl8k_stop(struct ieee80211_hw *hw)
+static void mwl8k_stop(struct ieee80211_hw *hw, bool suspend)
{
int rc;
struct mwl8k_stop_worker *worker;
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
index 48d81d9..d6660af 100644
--- a/drivers/net/wireless/p54/p54common.c
+++ b/drivers/net/wireless/p54/p54common.c
@@ -2087,7 +2087,7 @@ out:
return err;
}

-static void p54_stop(struct ieee80211_hw *dev)
+static void p54_stop(struct ieee80211_hw *dev, bool suspend)
{
struct p54_common *priv = dev->priv;
struct sk_buff *skb;
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 419b1b9..568a3c6 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -958,7 +958,7 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
*/
int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
int rt2x00mac_start(struct ieee80211_hw *hw);
-void rt2x00mac_stop(struct ieee80211_hw *hw);
+void rt2x00mac_stop(struct ieee80211_hw *hw, bool suspend);
int rt2x00mac_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
void rt2x00mac_remove_interface(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index c4c06b4..0540075 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -178,7 +178,7 @@ int rt2x00mac_start(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL_GPL(rt2x00mac_start);

-void rt2x00mac_stop(struct ieee80211_hw *hw)
+void rt2x00mac_stop(struct ieee80211_hw *hw, bool suspend)
{
struct rt2x00_dev *rt2x00dev = hw->priv;

diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 7e65d7c..d72debc 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -627,7 +627,7 @@ static int rtl8180_start(struct ieee80211_hw *dev)
return ret;
}

-static void rtl8180_stop(struct ieee80211_hw *dev)
+static void rtl8180_stop(struct ieee80211_hw *dev, bool suspend)
{
struct rtl8180_priv *priv = dev->priv;
u8 reg;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index 6499ccc..878bb6a 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -991,7 +991,7 @@ static int rtl8187_start(struct ieee80211_hw *dev)
return 0;
}

-static void rtl8187_stop(struct ieee80211_hw *dev)
+static void rtl8187_stop(struct ieee80211_hw *dev, bool suspend)
{
struct rtl8187_priv *priv = dev->priv;
struct sk_buff *skb;
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 603d611..dc3e804 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -363,7 +363,7 @@ out:
return ret;
}

-static void wl12xx_op_stop(struct ieee80211_hw *hw)
+static void wl12xx_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct wl12xx *wl = hw->priv;

diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6bdb170..e553a64 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -256,7 +256,7 @@ out:
return r;
}

-static void zd_op_stop(struct ieee80211_hw *hw)
+static void zd_op_stop(struct ieee80211_hw *hw, bool suspend)
{
struct zd_mac *mac = zd_hw_mac(hw);
struct zd_chip *chip = &mac->chip;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index d10ed17..9145cbf 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1296,10 +1296,12 @@ enum ieee80211_ampdu_mlme_action {
* Must be implemented.
*
* @stop: Called after last netdevice attached to the hardware
- * is disabled. This should turn off the hardware (at least
- * it must turn off frame reception.)
- * May be called right after add_interface if that rejects
- * an interface.
+ * is disabled or during suspend. This should turn off the
+ * hardware (at least it must turn off frame reception) unless
+ * the device wants to enable Wake-on-Wireless-LAN.
+ * This may be called right after add_interface if that rejects
+ * an interface. To assist drivers with WoW we inform the driver
+ * whether or not the stop call is for suspend.
* Must be implemented.
*
* @add_interface: Called when a netdevice attached to the hardware is
@@ -1421,7 +1423,7 @@ enum ieee80211_ampdu_mlme_action {
struct ieee80211_ops {
int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb);
int (*start)(struct ieee80211_hw *hw);
- void (*stop)(struct ieee80211_hw *hw);
+ void (*stop)(struct ieee80211_hw *hw, bool suspend);
int (*add_interface)(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
void (*remove_interface)(struct ieee80211_hw *hw,
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 3912b53..ba6eca1 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -16,7 +16,7 @@ static inline int drv_start(struct ieee80211_local *local)

static inline void drv_stop(struct ieee80211_local *local)
{
- local->ops->stop(&local->hw);
+ local->ops->stop(&local->hw, local->suspended);
}

static inline int drv_add_interface(struct ieee80211_local *local,
--
1.6.0.6


2009-05-13 09:21:01

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC v2 5/5] ath9k: Add Wake-on-Wireless-LAN support

This adds WoWLAN suppport for ath9k. We start by enabling only magic
packet WoWLAN. You can use regular ethernet Wake-on-LAN applications
to trigger a WoWLAN event.

Signed-off-by: Luis R. Rodriguez <[email protected]>
---
drivers/net/wireless/ath/ath9k/Makefile | 1 +
drivers/net/wireless/ath/ath9k/ath9k.h | 17 +
drivers/net/wireless/ath/ath9k/debug.c | 1 -
drivers/net/wireless/ath/ath9k/debug.h | 1 +
drivers/net/wireless/ath/ath9k/hw.c | 22 ++
drivers/net/wireless/ath/ath9k/hw.h | 21 ++
drivers/net/wireless/ath/ath9k/initvals.h | 31 ++
drivers/net/wireless/ath/ath9k/main.c | 21 ++
drivers/net/wireless/ath/ath9k/pci.c | 105 ++++++
drivers/net/wireless/ath/ath9k/reg.h | 154 ++++++++
drivers/net/wireless/ath/ath9k/wow.c | 543 +++++++++++++++++++++++++++++
11 files changed, 916 insertions(+), 1 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath9k/wow.c

diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 783bc39..d4a598d 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -14,5 +14,6 @@ ath9k-y += hw.o \
ath9k-$(CONFIG_PCI) += pci.o
ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o
ath9k-$(CONFIG_ATH9K_DEBUG) += debug.o
+ath9k-$(CONFIG_PM) += wow.o

obj-$(CONFIG_ATH9K) += ath9k.o
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 79a167c..34aebc6 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -599,12 +599,29 @@ struct ath_softc {
struct ath_rfkill rf_kill;
struct ath_ani ani;
struct ath9k_node_stats nodestats;
+#ifdef CONFIG_PM
+ bool wow_got_bmiss_intr;
+ bool wow_sleep_proc_intr;
+ u32 wow_intr_before_sleep;
+#endif
#ifdef CONFIG_ATH9K_DEBUG
struct ath9k_debug debug;
#endif
struct ath_bus_ops *bus_ops;
};

+#ifdef CONFIG_PM
+/*
+ * WoW trigger types
+ */
+#define AH_WOW_USER_PATTERN_EN 0x1
+#define AH_WOW_MAGIC_PATTERN_EN 0x2
+#define AH_WOW_LINK_CHANGE 0x4
+#define AH_WOW_BEACON_MISS 0x8
+#define AH_WOW_MAX_EVENTS 4
+
+#endif
+
struct ath_wiphy {
struct ath_softc *sc; /* shared for all virtual wiphys */
struct ieee80211_hw *hw;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 97df20c..43ddce7 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -493,7 +493,6 @@ static const struct file_operations fops_wiphy = {
.owner = THIS_MODULE
};

-
int ath9k_init_debug(struct ath_softc *sc)
{
sc->debug.debug_mask = ath9k_debug;
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index 23298b9..29ec232 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -29,6 +29,7 @@ enum ATH_DEBUG {
ATH_DBG_BEACON = 0x00000100,
ATH_DBG_CONFIG = 0x00000200,
ATH_DBG_FATAL = 0x00000400,
+ ATH_DBG_POWER_MGT = 0x00000800,
ATH_DBG_ANY = 0xffffffff
};

diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 4acfab5..719b31d 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -718,6 +718,12 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
ARRAY_SIZE(ar9285PciePhy_clkreq_always_on_L1_9285_1_2),
2);
}
+#ifdef CONFIG_PM
+ /* SerDes values during WOW sleep */
+ INIT_INI_ARRAY(&ah->iniPcieSerdesWow,
+ ar9285PciePhy_AWOW_9285_1_2,
+ ARRAY_SIZE(ar9285PciePhy_AWOW_9285_1_2), 2);
+#endif
} else if (AR_SREV_9285_10_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285,
ARRAY_SIZE(ar9285Modes_9285), 6);
@@ -748,6 +754,12 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
ar9280PciePhy_clkreq_always_on_L1_9280,
ARRAY_SIZE(ar9280PciePhy_clkreq_always_on_L1_9280), 2);
}
+#ifdef CONFIG_PM
+ /* SerDes values during WOW sleep */
+ INIT_INI_ARRAY(&ah->iniPcieSerdesWow, ar9280PciePhy_AWOW_9280,
+ ARRAY_SIZE(ar9280PciePhy_AWOW_9280), 2);
+#endif
+
INIT_INI_ARRAY(&ah->iniModesAdditional,
ar9280Modes_fast_clock_9280_2,
ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), 3);
@@ -893,6 +905,16 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc,
goto bad;
}

+#ifdef CONFIG_PM
+ /* WOW */
+ ath9k_wow_set_gpio_reset_low(ah);
+ /* Clear the Wow Status */
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, REG_READ(ah, AR_PCIE_PM_CTRL) |
+ AR_PMCTRL_WOW_PME_CLR);
+ REG_WRITE(ah, AR_WOW_PATTERN_REG,
+ AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN_REG)));
+#endif
+
if (AR_SREV_9285(ah))
ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S);
else
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index dd8508e..77a7346 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -521,6 +521,11 @@ struct ath_hw {
int initPDADC;
int PDADCdelta;

+#ifdef CONFIG_PM
+ /* WoW mask -- used to indicate which WoW we have enabled */
+ u32 ah_wowEventMask;
+#endif
+
struct ar5416IniArray iniModes;
struct ar5416IniArray iniCommon;
struct ar5416IniArray iniBank0;
@@ -536,6 +541,10 @@ struct ath_hw {
struct ar5416IniArray iniModesAdditional;
struct ar5416IniArray iniModesRxGain;
struct ar5416IniArray iniModesTxGain;
+#ifdef CONFIG_PM
+ /* SerDes values during WOW sleep */
+ struct ar5416IniArray iniPcieSerdesWow;
+#endif
};

/* Attach, Detach, Reset */
@@ -617,4 +626,16 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);

void ath9k_hw_btcoex_enable(struct ath_hw *ah);

+#ifdef CONFIG_PM
+
+/* WOW - Wake on Wireless */
+void ath9k_wow_set_gpio_reset_low(struct ath_hw *ah);
+/* Called when going to suspend/hibernate */
+int ath9k_hw_wow_enable(struct ath_hw *ah, u32 patternEnable);
+/* Called when coming back up from suspend/hibernation */
+u32 ath9k_hw_wow_wake_up(struct ath_hw *ah);
+const char *ath9k_hw_wow_event_to_string(u32 wow_event);
+
+#endif /* CONFIG_PM */
+
#endif
diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h
index e2f0a34..0cd23bb 100644
--- a/drivers/net/wireless/ath/ath9k/initvals.h
+++ b/drivers/net/wireless/ath/ath9k/initvals.h
@@ -3439,6 +3439,22 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = {
{0x00004044, 0x00000000 },
};

+#ifdef CONFIG_PM
+/* Auto generated PCI-E PHY config for AR9280 with WOW */
+static const u_int32_t ar9280PciePhy_AWOW_9280[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01ddffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+#endif
+
/* AR9285 */
static const u_int32_t ar9285Modes_9285[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
@@ -4846,3 +4862,18 @@ static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = {
{0x00004040, 0x00043007 },
{0x00004044, 0x00000000 },
};
+
+#ifdef CONFIG_PM
+static const u_int32_t ar9285PciePhy_AWOW_9285_1_2[][2] = {
+ {0x00004040, 0x9248fd00 },
+ {0x00004040, 0x24924924 },
+ {0x00004040, 0xa8000019 },
+ {0x00004040, 0x13160820 },
+ {0x00004040, 0xe5980560 },
+ {0x00004040, 0xc01ddffd },
+ {0x00004040, 0x1aaabe41 },
+ {0x00004040, 0xbe105554 },
+ {0x00004040, 0x00043007 },
+ {0x00004044, 0x00000000 },
+};
+#endif /* CONFIG_PM */
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index fa1acf1..5186d3a 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -560,6 +560,18 @@ irqreturn_t ath_isr(int irq, void *dev)
ath9k_hw_set_interrupts(ah, sc->imask);
}

+ if (status & ATH9K_INT_BMISS) {
+#ifdef CONFIG_PM
+ if (sc->wow_sleep_proc_intr) {
+ DPRINTF(sc, (ATH_DBG_POWER_MGT | ATH_DBG_INTERRUPT),
+ "during WoW we got a BMISS\n");
+ sc->wow_got_bmiss_intr = true;
+ sc->wow_sleep_proc_intr = false;
+ }
+#endif
+ DPRINTF(sc, ATH_DBG_INTERRUPT,
+ "spurious unattended beacon miss interrupt\n");
+ }
if (status & ATH9K_INT_TIM_TIMER) {
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
/* Clear RxAbort bit so that we can
@@ -2122,9 +2134,18 @@ static void ath9k_stop(struct ieee80211_hw *hw, bool suspend)
{
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct wiphy *wiphy = sc->hw->wiphy;

aphy->state = ATH_WIPHY_INACTIVE;

+#ifdef CONFIG_PM
+ if (suspend && wiphy->wow.triggers_enabled) {
+ DPRINTF(sc, ATH_DBG_ANY, "Leaving radio on during "
+ "suspend for WoW\n");
+ return;
+ }
+#endif
+
if (sc->sc_flags & SC_OP_INVALID) {
DPRINTF(sc, ATH_DBG_ANY, "Device not present\n");
return;
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 168411d..5dfb49d 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -133,6 +133,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)

pci_set_master(pdev);

+ device_init_wakeup(&pdev->dev, 1);
+ device_set_wakeup_enable(&pdev->dev, 0);
+
ret = pci_request_region(pdev, 0, "ath9k");
if (ret) {
dev_err(&pdev->dev, "PCI memory region reserve error\n");
@@ -183,6 +186,12 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)

sc->irq = pdev->irq;

+ /* Only for PCI */
+ hw->wiphy->wow.triggers_supported =
+ NL80211_WOW_TRIGGER_MAGIC_PACKET |
+ NL80211_WOW_TRIGGER_BMISS |
+ NL80211_WOW_TRIGGER_LINK_CHANGE;
+
ah = sc->sc_ah;
printk(KERN_INFO
"%s: Atheros AR%s MAC/BB Rev:%x "
@@ -219,11 +228,67 @@ static void ath_pci_remove(struct pci_dev *pdev)

#ifdef CONFIG_PM

+static u32 ath9k_wow_event_map(u32 cfg_wow_events)
+{
+ u32 wow_events = 0;
+
+ if (cfg_wow_events & NL80211_WOW_TRIGGER_MAGIC_PACKET)
+ wow_events |= AH_WOW_MAGIC_PATTERN_EN;
+ if (cfg_wow_events & NL80211_WOW_TRIGGER_BMISS)
+ wow_events |= AH_WOW_BEACON_MISS;
+ if (cfg_wow_events & NL80211_WOW_TRIGGER_LINK_CHANGE)
+ wow_events |= AH_WOW_LINK_CHANGE;
+
+ return wow_events;
+}
+
+static void ath9k_pci_wow_enable(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct wiphy *wiphy = sc->hw->wiphy;
+ int r;
+ u32 wake_up_events;
+
+ wake_up_events = ath9k_wow_event_map(wiphy->wow.triggers_enabled);
+
+ /* eventually we'll add this...
+ * if (wake_up_events & AH_WOW_USER_PATTERN_EN)
+ * ath9k_wow_create_pattern(sc);
+ */
+
+ /*
+ * To avoid false wake, we enable beacon miss interrupt only when
+ * we go to sleep. We save the current interrupt mask so that
+ * we can restore it after the system wakes up.
+ */
+ sc->wow_intr_before_sleep = ah->mask_reg;
+ ath9k_hw_set_interrupts(ah, ATH9K_INT_BMISS | ATH9K_INT_GLOBAL);
+ sc->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
+
+ r = ath9k_hw_wow_enable(ah, wake_up_events);
+ if (r) {
+ DPRINTF(sc, ATH_DBG_ANY,
+ "Unable to enable WoW\n");
+ return;
+ }
+
+ device_set_wakeup_enable(sc->dev, 1);
+
+ DPRINTF(sc, ATH_DBG_ANY,
+ "WoW enabled\n");
+
+ sc->wow_sleep_proc_intr = true;
+}
+
static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct wiphy *wiphy = sc->hw->wiphy;
+
+ if (wiphy->wow.triggers_enabled && device_can_wakeup(&pdev->dev))
+ ath9k_pci_wow_enable(sc);

ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);

@@ -234,16 +299,53 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state)

pci_save_state(pdev);
pci_disable_device(pdev);
+ if (wiphy->wow.triggers_enabled && device_can_wakeup(&pdev->dev)) {
+ pci_prepare_to_sleep(pdev);
+ return 0;
+ }
+ pci_wake_from_d3(pdev, !!wiphy->wow.triggers_enabled);
pci_set_power_state(pdev, PCI_D3hot);

return 0;
}

+static void ath9k_pci_wow_wake(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ u32 wow_status;
+
+ ath9k_hw_set_interrupts(ah, sc->wow_intr_before_sleep);
+ sc->imask = sc->wow_intr_before_sleep;
+
+ wow_status = ath9k_hw_wow_wake_up(sc->sc_ah);
+
+ if (wow_status)
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "Waking up due to WoW signal %s\n",
+ ath9k_hw_wow_event_to_string(wow_status));
+
+ if (sc->wow_got_bmiss_intr) {
+ /*
+ * Some devices may not pick beacon miss
+ * as the reason they woke up so we add that
+ * here for that shortcoming
+ */
+ wow_status |= AH_WOW_BEACON_MISS;
+ sc->wow_got_bmiss_intr = false;
+ }
+
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "WoW status: %d\n WoW reason: %s\n",
+ wow_status,
+ ath9k_hw_wow_event_to_string((wow_status & 0x0FFF)));
+}
+
static int ath_pci_resume(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_wiphy *aphy = hw->priv;
struct ath_softc *sc = aphy->sc;
+ struct wiphy *wiphy = sc->hw->wiphy;
int err;

err = pci_enable_device(pdev);
@@ -251,6 +353,9 @@ static int ath_pci_resume(struct pci_dev *pdev)
return err;
pci_restore_state(pdev);

+ if (wiphy->wow.triggers_enabled)
+ ath9k_pci_wow_wake(sc);
+
/* Enable LED */
ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index 5260524..387bbe7 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -665,6 +665,11 @@
#define AR_RC_HOSTIF 0x00000100

#define AR_WA 0x4004
+#define AR_WA_UNTIE_RESET_EN (1 << 15) /* Enable PCI Reset to POR (power-on-reset) */
+#define AR_WA_RESET_EN (1 << 18) /* Sw Control to enable PCI-Reset to POR (bit 15) */
+#define AR_WA_ANALOG_SHIFT (1 << 20)
+#define AR_WA_POR_SHORT (1 << 21) /* PCI-E Phy reset control */
+
#define AR9285_WA_DEFAULT 0x004a05cb
#define AR9280_WA_DEFAULT 0x0040073f
#define AR_WA_DEFAULT 0x0000073f
@@ -1508,4 +1513,153 @@ enum {
#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24)
#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28)

+/* WoW - Wake On Wireless */
+
+#define AR_PMCTRL_AUX_PWR_DET 0x10000000 /* Puts Chip in L2 state */
+#define AR_PMCTRL_D3COLD_VAUX 0x00800000
+#define AR_PMCTRL_HOST_PME_EN 0x00400000 /* Send OOB WAKE_L on WoW event */
+#define AR_PMCTRL_WOW_PME_CLR 0x00200000 /* Clear WoW event */
+#define AR_PMCTRL_PWR_STATE_MASK 0x0F000000 /* Power State Mask */
+#define AR_PMCTRL_PWR_STATE_D1D3 0x0F000000 /* Activate D1 and D3 */
+#define AR_PMCTRL_PWR_STATE_D0 0x08000000 /* Activate D0 */
+#define AR_PMCTRL_PWR_PM_CTRL_ENA 0x00008000 /* Enable power management */
+
+#define AR_WOW_BEACON_TIMO_MAX 0xFFFFFFFF /* Max. value for Beacon Timeout */
+
+/*
+ * MAC WoW Registers.
+ */
+#define AR_WOW_PATTERN_REG 0x825C
+#define AR_WOW_COUNT_REG 0x8260
+#define AR_WOW_BCN_EN_REG 0x8270
+#define AR_WOW_BCN_TIMO_REG 0x8274
+#define AR_WOW_KEEP_ALIVE_TIMO_REG 0x8278
+#define AR_WOW_KEEP_ALIVE_REG 0x827C
+#define AR_WOW_US_SCALAR_REG 0x8284
+#define AR_WOW_KEEP_ALIVE_DELAY_REG 0x8288
+#define AR_WOW_PATTERN_MATCH_REG 0x828C
+#define AR_WOW_PATTERN_OFF1_REG 0x8290 /* Pattern bytes 0 -> 3 */
+#define AR_WOW_PATTERN_OFF2_REG 0x8294 /* Pattern bytes 4 -> 7 */
+/* For AR9285 or Later version of chips */
+#define AR_WOW_EXACT_REG 0x829C
+#define AR_WOW_LENGTH1_REG 0x8360
+#define AR_WOW_LENGTH2_REG 0x8364
+/* Register to enable pattern match for less than 256 bytes packets */
+#define AR_WOW_PATTERN_MATCH_LT_256B_REG 0x8368
+
+/* AR_WOW_PATTERN_REG Values */
+#define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 27) /* in usecs */
+#define AR_WOW_MAC_INTR_EN 0x00040000
+#define AR_WOW_MAGIC_EN 0x00010000
+#define AR_WOW_PATTERN_EN(x) ((x & 0xff) << 0)
+#define AR_WOW_PATTERN_FOUND_SHIFT 8
+#define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PATTERN_FOUND_SHIFT))
+#define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PATTERN_FOUND_SHIFT)
+#define AR_WOW_MAGIC_PAT_FOUND 0x00020000
+#define AR_WOW_MAC_INTR 0x00080000
+#define AR_WOW_KEEP_ALIVE_FAIL 0x00100000
+#define AR_WOW_BEACON_FAIL 0x00200000
+
+#define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \
+ AR_WOW_MAGIC_PAT_FOUND | \
+ AR_WOW_KEEP_ALIVE_FAIL | \
+ AR_WOW_BEACON_FAIL))
+#define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \
+ AR_WOW_MAGIC_EN | \
+ AR_WOW_MAC_INTR_EN | \
+ AR_WOW_BEACON_FAIL | \
+ AR_WOW_KEEP_ALIVE_FAIL))
+
+/* AR_WOW_COUNT_REG Values */
+#define AR_WOW_AIFS_CNT(x) ((x & 0xff) << 0)
+#define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8)
+#define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16)
+
+/* AR_WOW_BCN_EN_REG */
+#define AR_WOW_BEACON_FAIL_EN 0x00000001
+
+/* AR_WOW_BCN_TIMO_REG */
+#define AR_WOW_BEACON_TIMO 0x40000000 /* Valid if BCN_EN is set */
+
+/* AR_WOW_KEEP_ALIVE_TIMO_REG */
+#define AR_WOW_KEEP_ALIVE_TIMO 0x00007A12
+#define AR_WOW_KEEP_ALIVE_NEVER 0xFFFFFFFF
+
+/* AR_WOW_KEEP_ALIVE_REG */
+#define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001
+#define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002
+
+/* AR_WOW_KEEP_ALIVE_DELAY_REG */
+#define AR_WOW_KEEP_ALIVE_DELAY 0x000003E8 /* 1 msec */
+
+/*
+ * Keep it long for Beacon workaround - ensures no false alarm
+ */
+#define AR_WOW_BMISSTHRESHOLD 0x20
+
+/* AR_WOW_PATTERN_MATCH_REG */
+#define AR_WOW_PAT_END_OF_PKT(x) ((x & 0xf) << 0)
+#define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8)
+
+/*
+ * Default values for Wow Configuration for backoff, aifs, slot, keep-alive, etc
+ * to be programmed into various registers.
+ */
+#define AR_WOW_PAT_BACKOFF 0x00000004 /* AR_WOW_PATTERN_REG */
+#define AR_WOW_CNT_AIFS_CNT 0x00000022 /* AR_WOW_COUNT_REG */
+#define AR_WOW_CNT_SLOT_CNT 0x00000009 /* AR_WOW_COUNT_REG */
+/*
+ * Keepalive count applicable for AR9280 2.0 and above.
+ */
+#define AR_WOW_CNT_KA_CNT 0x00000008 /* AR_WOW_COUNT_REG */
+
+/* WoW - Transmit buffer for keep alive frames */
+#define AR_WOW_TRANSMIT_BUFFER 0xE000 /* E000 - EFFC */
+
+#define AR_WOW_KA_DESC_WORD2 0xE000
+#define AR_WOW_KA_DESC_WORD3 0xE004
+#define AR_WOW_KA_DESC_WORD4 0xE008
+#define AR_WOW_KA_DESC_WORD5 0xE00C
+#define AR_WOW_KA_DESC_WORD6 0xE010
+#define AR_WOW_KA_DESC_WORD7 0xE014
+#define AR_WOW_KA_DESC_WORD8 0xE018
+#define AR_WOW_KA_DESC_WORD9 0xE01C
+#define AR_WOW_KA_DESC_WORD10 0xE020
+#define AR_WOW_KA_DESC_WORD11 0xE024
+#define AR_WOW_KA_DESC_WORD12 0xE028
+#define AR_WOW_KA_DESC_WORD13 0xE02C
+
+#define AR_WOW_KA_DATA_WORD0 0xE030
+#define AR_WOW_KA_DATA_WORD1 0xE034
+#define AR_WOW_KA_DATA_WORD2 0xE038
+#define AR_WOW_KA_DATA_WORD3 0xE03C
+#define AR_WOW_KA_DATA_WORD4 0xE040
+#define AR_WOW_KA_DATA_WORD5 0xE044
+
+/* WoW Transmit Buffer for patterns */
+#define AR_WOW_TB_PATTERN0 0xE100
+#define AR_WOW_TB_PATTERN1 0xE200
+#define AR_WOW_TB_PATTERN2 0xE300
+#define AR_WOW_TB_PATTERN3 0xE400
+#define AR_WOW_TB_PATTERN4 0xE500
+#define AR_WOW_TB_PATTERN5 0xE600
+#define AR_WOW_TB_PATTERN6 0xE700
+#define AR_WOW_TB_PATTERN7 0xE800
+#define AR_WOW_TB_MASK0 0xEC00
+#define AR_WOW_TB_MASK1 0xEC20
+#define AR_WOW_TB_MASK2 0xEC40
+#define AR_WOW_TB_MASK3 0xEC60
+#define AR_WOW_TB_MASK4 0xEC80
+#define AR_WOW_TB_MASK5 0xECa0
+#define AR_WOW_TB_MASK6 0xECC0
+#define AR_WOW_TB_MASK7 0xECE0
+
+/* Currently Pattern 0-7 are supported - so bit 0-7 are set */
+#define AR_WOW_PATTERN_SUPPORTED 0xFF
+#define AR_WOW_LENGTH_MAX 0xFF
+#define AR_WOW_LENGTH1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3)
+#define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LENGTH1_SHIFT(_i))
+#define AR_WOW_LENGTH2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3)
+#define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LENGTH2_SHIFT(_i))
+
#endif
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
new file mode 100644
index 0000000..11fee62
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2008-2009 Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ath9k.h"
+#include "reg.h"
+
+#ifdef CONFIG_PM
+
+/*
+ * This routine is called to configure the SerDes register for the
+ * AR9280 2.0 and above chip during WOW sleep.
+ */
+static void
+ath9k_ar928xConfigSerDes_WowSleep(struct ath_hw *ah)
+{
+ unsigned int i;
+
+ /*
+ * For WOW sleep, we reprogram the SerDes so that the PLL and CHK REQ
+ * are both enabled. This uses more power but in certain cases this
+ * is required as otherwise WOW sleep is unstable and chip may
+ * disappears.
+ */
+ for (i = 0; i < ah->iniPcieSerdesWow.ia_rows; i++)
+ REG_WRITE(ah,
+ INI_RA(&ah->iniPcieSerdesWow, i, 0),
+ INI_RA(&ah->iniPcieSerdesWow, i, 1));
+ udelay(1000);
+}
+
+static bool ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
+{
+ u32 frame_len = 28;
+ u32 tpc = 0x3f;
+ u32 antenna_mode = 1;
+ u32 transmit_rate;
+ u32 frame_type = 0x2; /* Frame Type -> Data */
+ u32 sub_type = 0x4; /* Subtype -> Null Data */
+ u32 to_ds = 1;
+ u32 duration_id = 0x3d;
+ u8 *StaMacAddr, *ApMacAddr;
+ u8 *addr1, *addr2, *addr3;
+ u32 ctl[12] = { 0 };
+ u32 data_word0 = 0, data_word1 = 0, data_word2 = 0,
+ data_word3 = 0, data_word4 = 0, data_word5 = 0;
+ u32 i;
+
+ StaMacAddr = (u8 *)ah->macaddr;
+ ApMacAddr = (u8 *)ah->ah_sc->curbssid;
+ addr2 = StaMacAddr;
+ addr1 = addr3 = ApMacAddr;
+
+ /*
+ * XXX: we need a way to determine if the AP we're on
+ * is using CCK only and if so use this:
+ * transmit_rate = 0x1B; // CCK_1M
+ * For now we just assume your AP supports OFDM
+ */
+ transmit_rate = 0xB; /* OFDM_6M */
+
+ /* Set the Transmit Buffer. */
+ ctl[0] = (frame_len | (tpc << 16)) + (antenna_mode << 25);
+ ctl[1] = 0;
+ ctl[2] = 0x7 << 16; /* tx_tries0 */
+ ctl[3] = transmit_rate;
+ ctl[4] = 0;
+ ctl[7] = ah->txchainmask << 2;
+
+ for (i = 0; i < 12; i++)
+ REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
+
+ data_word0 = (frame_type << 2) | (sub_type << 4) |
+ (to_ds << 8) | (duration_id << 16);
+ data_word1 = (((u32)addr1[3] << 24) | ((u32)addr1[2] << 16) |
+ ((u32)addr1[1]) << 8 | ((u32)addr1[0]));
+ data_word2 = (((u32)addr2[1] << 24) | ((u32)addr2[0] << 16) |
+ ((u32)addr1[5]) << 8 | ((u32)addr1[4]));
+ data_word3 = (((u32)addr2[5] << 24) | ((u32)addr2[4] << 16) |
+ ((u32)addr2[3]) << 8 | ((u32)addr2[2]));
+ data_word4 = (((u32)addr3[3] << 24) | ((u32)addr3[2] << 16) |
+ ((u32)addr3[1]) << 8 | (u32)addr3[0]);
+ data_word5 = (((u32)addr3[5]) << 8 | ((u32)addr3[4]));
+
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD0, data_word0);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD1, data_word1);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD2, data_word2);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD3, data_word3);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD4, data_word4);
+ REG_WRITE(ah, AR_WOW_KA_DATA_WORD5, data_word5);
+
+ return true;
+}
+
+/* TBD: Should querying hw for hardware capability */
+#define MAX_PATTERN_SIZE 256
+#define MAX_PATTERN_MASK_SIZE 32
+
+/* Deducting the disassociate/deauthenticate packets */
+#define MAX_NUM_USER_PATTERN 6
+
+# if 0
+static void ath9k_wow_apply_pattern(struct ath_hw *ah,
+ u8 *pAthPattern, u8 *pAthMask,
+ int pattern_count, u32 athPatternLen)
+{
+ unsigned int i;
+ u32 reg_pat[] = {
+ AR_WOW_TB_PATTERN0,
+ AR_WOW_TB_PATTERN1,
+ AR_WOW_TB_PATTERN2,
+ AR_WOW_TB_PATTERN3,
+ AR_WOW_TB_PATTERN4,
+ AR_WOW_TB_PATTERN5,
+ AR_WOW_TB_PATTERN6,
+ AR_WOW_TB_PATTERN7
+ };
+ u32 reg_mask[] = {
+ AR_WOW_TB_MASK0,
+ AR_WOW_TB_MASK1,
+ AR_WOW_TB_MASK2,
+ AR_WOW_TB_MASK3,
+ AR_WOW_TB_MASK4,
+ AR_WOW_TB_MASK5,
+ AR_WOW_TB_MASK6,
+ AR_WOW_TB_MASK7
+ };
+ u32 pattern_val;
+ u32 mask_val;
+ u8 mask_bit = 0x1;
+ u8 pattern;
+
+ /* TBD: should check count by querying the hardware capability */
+ if (pattern_count >= MAX_NUM_USER_PATTERN)
+ return;
+
+ pattern = (u8)REG_READ(ah, AR_WOW_PATTERN_REG);
+ pattern = pattern | (mask_bit << pattern_count);
+ REG_WRITE(ah, AR_WOW_PATTERN_REG, pattern);
+
+ /* Set the registers for pattern */
+ for (i = 0; i < MAX_PATTERN_SIZE; i+=4) {
+ pattern_val = (((u32)pAthPattern[i]) |
+ ((u32)pAthPattern[i+1] << 8) |
+ ((u32)pAthPattern[i+2] << 16) |
+ ((u32)pAthPattern[i+3] << 24));
+ REG_WRITE(ah, (reg_pat[pattern_count] + i), pattern_val);
+ }
+
+ /* Set the registers for mask */
+ for (i = 0; i < MAX_PATTERN_MASK_SIZE; i+=4) {
+ mask_val = (((u32)pAthMask[i]) |
+ ((u32)pAthMask[i+1] << 8) |
+ ((u32)pAthMask[i+2] << 16) |
+ ((u32)pAthMask[i+3] << 24));
+ REG_WRITE(ah, (reg_mask[pattern_count] + i), mask_val);
+ }
+
+ if (AR_SREV_9285_10_OR_LATER(ah)) {
+ /* Set the pattern length to be matched */
+ u32 val;
+ if (pattern_count < 4) {
+ /* Pattern 0-3 uses AR_WOW_LENGTH1_REG register */
+ val = REG_READ(ah, AR_WOW_LENGTH1_REG);
+ val = ((val & (~AR_WOW_LENGTH1_MASK(pattern_count))) |
+ ((athPatternLen & AR_WOW_LENGTH_MAX)
+ << AR_WOW_LENGTH1_SHIFT(pattern_count)));
+ REG_WRITE(ah, AR_WOW_LENGTH1_REG, val);
+ } else {
+ /* Pattern 4-7 uses AR_WOW_LENGTH2_REG register */
+ val = REG_READ(ah, AR_WOW_LENGTH2_REG);
+ val = ((val & (~AR_WOW_LENGTH2_MASK(pattern_count))) |
+ ((athPatternLen & AR_WOW_LENGTH_MAX)
+ << AR_WOW_LENGTH2_SHIFT(pattern_count)));
+ REG_WRITE(ah, AR_WOW_LENGTH2_REG, val);
+ }
+ }
+
+ ah->ah_wowEventMask |=
+ (1 << (pattern_count + AR_WOW_PATTERN_FOUND_SHIFT));
+}
+#endif
+
+static bool ath9k_set_power_mode_wow_sleep(struct ath_hw *ah)
+{
+ REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
+
+ REG_WRITE(ah, AR_CR, AR_CR_RXD); /* Set receive disable bit */
+ if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGT,
+ "dma failed to stop in 10ms\n"
+ "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n",
+ REG_READ(ah, AR_CR),
+ REG_READ(ah, AR_DIAG_SW));
+ return false;
+ } else {
+ REG_WRITE(ah, AR_RXDP, 0x0);
+
+ /* AR9280 2.0/2.1 WOW has sleep issue, do not set it to sleep */
+ if (AR_SREV_9280_20(ah))
+ return true;
+ else
+ REG_WRITE(ah, AR_RTC_FORCE_WAKE,
+ AR_RTC_FORCE_WAKE_ON_INT);
+ return true;
+ }
+}
+
+int ath9k_hw_wow_enable(struct ath_hw *ah, u32 patternEnable)
+{
+ u32 init_val, val, rval = 0;
+ /* Send a Keep-Alive frame every 900 millisec */
+ const int ka_timo = 900;
+ /* Delay of 4 millisec between two KeepAlive's */
+ const int ka_delay = 4;
+ u32 wow_event_mask;
+ bool all_triggers_set = true;
+
+ /*
+ * ah_wowEventMask is a mask to the AR_WOW_PATTERN_REG register to
+ * indicate which WOW events that we have enabled. The WOW Events
+ * are from the patternEnable in this function and pattern_count of
+ * ath9k_wow_apply_pattern()
+ */
+ wow_event_mask = ah->ah_wowEventMask;
+
+ /*
+ * Untie Power-On-Reset from the PCI-E Reset. When we are in WOW sleep,
+ * we do not want the Reset from the PCI-E to disturb our hw state.
+ */
+ if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress) {
+ /*
+ * We need to untie the internal POR (power-on-reset) to the
+ * external PCI-E reset. We also need to tie the PCI-E Phy
+ * reset to the PCI-E reset.
+ */
+ u32 wa_reg_val;
+ if (AR_SREV_9285(ah))
+ wa_reg_val = AR9285_WA_DEFAULT;
+ else
+ wa_reg_val = AR9280_WA_DEFAULT;
+ wa_reg_val = wa_reg_val & ~(AR_WA_UNTIE_RESET_EN);
+ wa_reg_val = wa_reg_val | AR_WA_RESET_EN | AR_WA_POR_SHORT;
+ REG_WRITE(ah, AR_WA, wa_reg_val);
+
+ if (!AR_SREV_9285(ah) || AR_SREV_9285_12_OR_LATER(ah)) {
+ /*
+ * For WOW sleep, we reprogram the SerDes so that the
+ * PLL and CHK REQ are both enabled. This uses more
+ * power but otherwise in certain cases, WOW sleep is
+ * unusable and chip may disappears.
+ */
+ ath9k_ar928xConfigSerDes_WowSleep(ah);
+ }
+ }
+
+ /*
+ * Set the power states appropriately and enable pme.
+ */
+ val = REG_READ(ah, AR_PCIE_PM_CTRL);
+ val |= AR_PMCTRL_HOST_PME_EN |
+ AR_PMCTRL_PWR_PM_CTRL_ENA |
+ AR_PMCTRL_AUX_PWR_DET;
+ val &= ~AR_PMCTRL_WOW_PME_CLR;
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+ /*
+ * Setup for:
+ * - beacon misses
+ * - magic pattern
+ * - keep alive timeout
+ * - pattern matching
+ */
+
+ /*
+ * Program some default values for keep-alives, beacon misses, etc.
+ */
+ init_val = REG_READ(ah, AR_WOW_PATTERN_REG);
+ val = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF) | init_val;
+ REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
+ rval = REG_READ(ah, AR_WOW_PATTERN_REG);
+
+ init_val = REG_READ(ah, AR_WOW_COUNT_REG);
+ val = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | \
+ AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | \
+ AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
+ REG_WRITE(ah, AR_WOW_COUNT_REG, val);
+ rval = REG_READ(ah, AR_WOW_COUNT_REG);
+
+
+ init_val = REG_READ(ah, AR_WOW_BCN_TIMO_REG);
+ if (patternEnable & AH_WOW_BEACON_MISS)
+ val = AR_WOW_BEACON_TIMO;
+ else
+ /* We are not using the beacon miss. Program a large value. */
+ val = AR_WOW_BEACON_TIMO_MAX;
+ REG_WRITE(ah, AR_WOW_BCN_TIMO_REG, val);
+ rval = REG_READ(ah, AR_WOW_BCN_TIMO_REG);
+ if ((patternEnable & AH_WOW_BEACON_MISS) &&
+ !(rval & AR_WOW_BEACON_TIMO))
+ all_triggers_set = false;
+
+ init_val = REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
+
+ /*
+ * Keep Alive Timo in ms.
+ */
+ if (patternEnable == 0)
+ val = AR_WOW_KEEP_ALIVE_NEVER;
+ else
+ val = ka_timo * 32;
+ REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO_REG, val);
+ rval = REG_READ(ah, AR_WOW_KEEP_ALIVE_TIMO_REG);
+
+ init_val = REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
+ /*
+ * Keep Alive delay in us.
+ */
+ val = ka_delay * 1000;
+ REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY_REG, val);
+ rval = REG_READ(ah, AR_WOW_KEEP_ALIVE_DELAY_REG);
+
+ /*
+ * Create KeepAlive Pattern to respond to beacons.
+ */
+ ath9k_wow_create_keep_alive_pattern(ah);
+
+ /*
+ * Configure Mac Wow Registers.
+ */
+
+ val = REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
+ /*
+ * Send keep alive timeouts anyway.
+ */
+ val &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS;
+
+ if (patternEnable & AH_WOW_LINK_CHANGE) {
+ val &= ~ AR_WOW_KEEP_ALIVE_FAIL_DIS;
+ wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
+ } else
+ val |= AR_WOW_KEEP_ALIVE_FAIL_DIS;
+
+ REG_WRITE(ah, AR_WOW_KEEP_ALIVE_REG, val);
+ val = REG_READ(ah, AR_WOW_KEEP_ALIVE_REG);
+ if ((patternEnable & AH_WOW_LINK_CHANGE) &&
+ (val & AR_WOW_KEEP_ALIVE_FAIL_DIS))
+ all_triggers_set = false;
+ val = REG_READ(ah, AR_WOW_BCN_EN_REG);
+
+ /*
+ * We are relying on a bmiss failure. Ensure we have enough
+ * threshold to prevent false positives.
+ */
+ REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
+ AR_WOW_BMISSTHRESHOLD);
+
+ /*
+ * Beacon miss & user pattern events do not work on AR5416.
+ * We enable beacon miss wow pattern only for AR9280...
+ */
+ if (!AR_SREV_9280_10_OR_LATER(ah))
+ patternEnable &= ~AH_WOW_BEACON_MISS;
+
+ if (patternEnable & AH_WOW_BEACON_MISS) {
+ val |= AR_WOW_BEACON_FAIL_EN;
+ wow_event_mask |= AR_WOW_BEACON_FAIL;
+ } else
+ val &= ~AR_WOW_BEACON_FAIL_EN;
+
+ REG_WRITE(ah, AR_WOW_BCN_EN_REG, val);
+ val = REG_READ(ah, AR_WOW_BCN_EN_REG);
+ if ((patternEnable & AH_WOW_BEACON_MISS) &&
+ !(val & AR_WOW_BEACON_FAIL_EN))
+ all_triggers_set = false;
+
+ /*
+ * Enable the magic packet registers.
+ */
+ val = REG_READ(ah, AR_WOW_PATTERN_REG);
+ if (patternEnable & AH_WOW_MAGIC_PATTERN_EN) {
+ val |= AR_WOW_MAGIC_EN;
+ wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
+ } else
+ val &= ~AR_WOW_MAGIC_EN;
+
+ val |= AR_WOW_MAC_INTR_EN;
+ REG_WRITE(ah, AR_WOW_PATTERN_REG, val);
+ val = REG_READ(ah, AR_WOW_PATTERN_REG);
+
+ /* Lets be a little more verbose about this one */
+ if (patternEnable & AH_WOW_MAGIC_PATTERN_EN) {
+ if (val & AR_WOW_MAGIC_EN) {
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGT,
+ "WoW: Magic pattern trigger set\n");
+ } else {
+ all_triggers_set = false;
+ DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGT,
+ "WoW: Unable to enable magic "
+ "pattern trigger\n");
+ }
+ }
+
+ /*
+ * For AR9285 and later version of the chips
+ * enable wow pattern match for packets less than
+ * 256 bytes for all patterns.
+ */
+ if (AR_SREV_9285_10_OR_LATER(ah))
+ REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B_REG,
+ AR_WOW_PATTERN_SUPPORTED);
+
+ /*
+ * Set the power states appropriately and enable pme.
+ */
+ val = REG_READ(ah, AR_PCIE_PM_CTRL);
+ val |= AR_PMCTRL_PWR_STATE_D1D3 |
+ AR_PMCTRL_HOST_PME_EN |
+ AR_PMCTRL_PWR_PM_CTRL_ENA;
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+ ath9k_set_power_mode_wow_sleep(ah);
+
+ ah->ah_wowEventMask = wow_event_mask;
+
+ if (!all_triggers_set)
+ return -EIO;
+
+ return 0;
+}
+
+u32 ath9k_hw_wow_wake_up(struct ath_hw *ah)
+{
+ u32 wowStatus = 0;
+ u32 val = 0, rval;
+
+ /*
+ * Read the WOW Status register to know the wakeup reason.
+ */
+ rval = REG_READ(ah, AR_WOW_PATTERN_REG);
+ val = AR_WOW_STATUS(rval);
+
+ /*
+ * Mask only the WOW events that we have enabled. Sometimes, we have
+ * spurious WOW events from the AR_WOW_PATTERN_REG register. This mask
+ * will clean it up.
+ */
+ val &= ah->ah_wowEventMask;
+
+ if (val) {
+ if (val & AR_WOW_MAGIC_PAT_FOUND)
+ wowStatus |= AH_WOW_MAGIC_PATTERN_EN;
+ if (AR_WOW_PATTERN_FOUND(val))
+ wowStatus |= AH_WOW_USER_PATTERN_EN;
+ if (val & AR_WOW_KEEP_ALIVE_FAIL)
+ wowStatus |= AH_WOW_LINK_CHANGE;
+ if (val & AR_WOW_BEACON_FAIL)
+ wowStatus |= AH_WOW_BEACON_MISS;
+ }
+
+ /*
+ * Set and clear WOW_PME_CLEAR registers for the chip to generate next
+ * wow signal. Disable D3 before accessing other registers ?
+ */
+ val = REG_READ(ah, AR_PCIE_PM_CTRL);
+ /* Do we have to check the bit value 0x01000000 (7-10) ?? */
+ val &= ~AR_PMCTRL_PWR_STATE_D1D3;
+ val |= AR_PMCTRL_WOW_PME_CLR;
+ REG_WRITE(ah, AR_PCIE_PM_CTRL, val);
+
+ /*
+ * Clear all events.
+ */
+ REG_WRITE(ah, AR_WOW_PATTERN_REG,
+ AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN_REG)));
+
+ /*
+ * Tie reset register.
+ * NB: Not tieing it back might have some repurcussions.
+ */
+ if (AR_SREV_9280_10_OR_LATER(ah)) {
+ REG_WRITE(ah, AR_WA, REG_READ(ah, AR_WA) |
+ AR_WA_UNTIE_RESET_EN |
+ AR_WA_POR_SHORT |
+ AR_WA_RESET_EN);
+ }
+
+ /* Restore the Beacon Threshold to init value */
+ REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
+
+ /*
+ * Restore the way the PCI-E Reset, Power-On-Reset, external
+ * PCIE_POR_SHORT pins are tied to its original value. Previously
+ * just before WOW sleep, we untie the PCI-E Reset to our Chip's
+ * Power On Reset so that any PCI-E reset from the bus will not
+ * reset our chip.
+ */
+ if (AR_SREV_9280_20_OR_LATER(ah) && ah->is_pciexpress)
+ ath9k_hw_configpcipowersave(ah, 0);
+
+ ah->ah_wowEventMask = 0;
+
+ return wowStatus;
+}
+
+void ath9k_wow_set_gpio_reset_low(struct ath_hw *ah)
+{
+ u32 val;
+
+ val = REG_READ(ah, AR_GPIO_OE_OUT);
+ val |= (1 << (2 * 2));
+ REG_WRITE(ah, AR_GPIO_OE_OUT, val);
+ val = REG_READ(ah, AR_GPIO_OE_OUT);
+ val = REG_READ(ah,AR_GPIO_IN_OUT );
+}
+
+const char *
+ath9k_hw_wow_event_to_string(u32 wow_event)
+{
+ if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
+ return "Magic pattern";
+ if (wow_event & AH_WOW_USER_PATTERN_EN)
+ return "User pattern";
+ if (wow_event & AH_WOW_LINK_CHANGE)
+ return "Link change";
+ if (wow_event & AH_WOW_BEACON_MISS)
+ return "Beacon miss";
+ return "Uknown event";
+}
+
+#endif /* CONFIG_PM */
--
1.6.0.6


2009-05-13 21:31:09

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 2:09 PM, Bob Copeland <[email protected]> wro=
te:
> On Wed, May 13, 2009 at 2:13 PM, Luis R. Rodriguez
> <[email protected]> wrote:
>> Agreed, however ath9k is the only card I know how WoW works so far.
>
> Understood.
>
>> Thoughts?
>
> None really, it just seemed adding flags to stop() is a less-than-ide=
al
> interface. =C2=A0Hmm, maybe there is inspiration to be had from plain=
old
> ethernet drivers...
>
> <looks quickly>
>
> The one example I looked at, amd8111e, used set_wol/get_wol callbacks
> from ethtool to set the option, then did something special in its bus
> suspend method for the magic. *shrug*

Heh I reviewed ethernet and it was a bit messy. So you're right we
should try to make it very consistent and easy to follow and document
it well. I tend to think listening to johill is good :)

Luis

2009-05-13 22:23:11

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 3/5] cfg80211: add WoW support

On Wed, 2009-05-13 at 11:14 -0700, Luis R. Rodriguez wrote:

> > > +enum nl80211_wow_triggers {
> > > + NL80211_WOW_TRIGGER_MAGIC_PACKET = 1 << 0,
> > > + NL80211_WOW_TRIGGER_BMISS = 1 << 1,
> > > + NL80211_WOW_TRIGGER_LINK_CHANGE = 1 << 2,
> > > + NL80211_WOW_TRIGGER_PATTERN = 1 << 3,
> > > +};
> >
> > Seems like you need to have a way to set the pattern and magic packet?
>
> Yeah TDB -- the code for that is a bit complex and I don't understand how
> things are being broken down yet. We could just not have the cabability
> present until that's properly figured out.

Yeah I think that would be preferable, maybe even not define the bits
until we figure out what else we need. It might not actually make sense
to use bits here, for example if you need a magic packet to wake up it
might make sense to have just a magic packet netlink attribute that, if
userspace passes it, defines that magic packet with that packet should
be used, instead of requiring that the user sets both the bit and gives
the packet. etc.

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-05-13 18:14:49

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 3/5] cfg80211: add WoW support

On Wed, May 13, 2009 at 02:55:15AM -0700, Johannes Berg wrote:
> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
>
> > +/**
> > + * enum nl80211_wow_triggers - Wake-on-Wireless-LAN triggers
> > + *
> > + * NL80211_WOW_TRIGGER_MAGIC_PACKET: a wake signal will be sent to the
> > + * devices if a magic packet is received.
> > + * NL80211_WOW_TRIGGER_BMISS: WoW signal will be sent to the device when
> > + * a beacon has been missed by the associated AP.
> > + * NL80211_WOW_TRIGGER_LINK_CHANGE: a wake signal will be sent to
> > + * the device if a link change is detected on the device.
> > + * NL80211_WOW_TRIGGER_USER_PATTERN: a wake sigal will be sent to the
> > + * device if a user configurable pattern is received by
> > + * the device.
> > + */
> > +enum nl80211_wow_triggers {
> > + NL80211_WOW_TRIGGER_MAGIC_PACKET = 1 << 0,
> > + NL80211_WOW_TRIGGER_BMISS = 1 << 1,
> > + NL80211_WOW_TRIGGER_LINK_CHANGE = 1 << 2,
> > + NL80211_WOW_TRIGGER_PATTERN = 1 << 3,
> > +};
>
> Seems like you need to have a way to set the pattern and magic packet?

Yeah TDB -- the code for that is a bit complex and I don't understand how
things are being broken down yet. We could just not have the cabability
present until that's properly figured out.

Luis

2009-05-13 23:16:26

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 3:27 PM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2009-05-13 at 14:00 -0400, Bob Copeland wrote:
>> On Wed, May 13, 2009 at 1:20 PM, Luis R. Rodriguez
>> <[email protected]> wrote:
>> > On Wed, May 13, 2009 at 2:53 AM, Johannes Berg
>> > <[email protected]> wrote:
>> >> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
>> >>> We inform the drivers so they can take appropriate actions durin=
g
>> >>> suspend for WoW support.
>> >>
>> >>> -static void ar9170_op_stop(struct ieee80211_hw *hw)
>> >>> +static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspen=
d)
>>
>> Just curious, why can't the bus suspend method just turn the radio b=
ack on?
>> You have to set up the wow parameters later anyway.
>>
>> I think it'd be cleaner if the stack knew about WoW somehow rather t=
han
>> doing stuff behind its back in the driver. =C2=A0Then suspend/resume=
could do
>> the right thing (e.g. ops->wow_enable() instead of stop) if the driv=
er was
>> WoW-capable.
>
> That's a good point. Somehow I thought this was intimately tied to
> suspend. And I think for cfg80211 that makes sense, since cfg80211
> doesn't do much for suspend. But for mac80211 you're right in that it
> would make sense to
> =C2=A0* keep the radio on

This is implicit by not calling stop, right?

> =C2=A0* not call stop

This makes sense for ath9k so far -- I was just exiting out.

> =C2=A0* maybe not remove all the sta info structs

Which ones? In my tests I'm just not removing any.

> =C2=A0* and whatever else may be necessary -- that might even depend =
on the
> =C2=A0 wow mode

So lets start with a basic goal -- magic packet. If you think about it
though if you disassociate you won't get this magic packet so I guess
that's also why we have link-change trigger (or called disassoc
trigger on some other hardware?). Anyway out of these link change and
magic packet seem like a reasonable goal to get working first. For
that besides the above I'm not sure what else is required.

I'm building this stuff now on a box with a card that is supposed to
have WoW working so I'll know for sure if we are missing something
soon.

Luis

2009-05-13 17:20:55

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 2:53 AM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
>> We inform the drivers so they can take appropriate actions during
>> suspend for WoW support.
>
>> -static void ar9170_op_stop(struct ieee80211_hw *hw)
>> +static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)
>
> Why just a 'suspend' bool? Why not pass the WoW information in here,
> instead of adding a new config and stuff for it? WoW information is o=
nly
> relevant during suspend.
>
> The way you have it, a driver needs to
> =C2=A0* keep track of the wow config
> =C2=A0* check whether a stop is due to suspend
> =C2=A0* apply wow config during stop
>
> If you passed the wow config in some struct to _stop() [and NULL if n=
ot
> for wow] then the driver would only need to
>
> =C2=A0* check if for wow and apply config during stop

Reason for not passing it is its accessible through the wiphy. All we
really need from mac80211 is if it changed (some devices may need
this, and will be more useful for the user pattern handling) and if we
are stopping due to suspend.

What advantage do we have if we stuff a wow struct instead of having
the driver read the wiphy wow info?

Luis

2009-05-13 09:21:00

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC v2 4/5] mac80211: add WoW support

Signed-off-by: Luis R. Rodriguez <[email protected]>
---
include/net/mac80211.h | 5 ++++-
net/mac80211/cfg.c | 11 +++++++++++
net/mac80211/pm.c | 6 ++++++
net/mac80211/util.c | 8 ++++++++
4 files changed, 29 insertions(+), 1 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 9145cbf..17aeb99 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -536,6 +536,7 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_CHANNEL: the channel/channel_type changed
* @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
* @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
+ * @IEEE80211_CONF_CHANGE_WOW: Wake-on-Wireless LAN triggers have changed
*/
enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_RADIO_ENABLED = BIT(0),
@@ -547,6 +548,7 @@ enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
IEEE80211_CONF_CHANGE_IDLE = BIT(8),
+ IEEE80211_CONF_CHANGE_WOW = BIT(9),
};

static inline __deprecated enum ieee80211_conf_changed
@@ -1301,7 +1303,8 @@ enum ieee80211_ampdu_mlme_action {
* the device wants to enable Wake-on-Wireless-LAN.
* This may be called right after add_interface if that rejects
* an interface. To assist drivers with WoW we inform the driver
- * whether or not the stop call is for suspend.
+ * whether or not the stop call is for suspend. Driver wishing
+ * to retrieve WoW enabled events can use the wiphy->wow.triggers_enabled.
* Must be implemented.
*
* @add_interface: Called when a netdevice attached to the hardware is
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index be86e15..a749908 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1146,6 +1146,16 @@ static int ieee80211_resume(struct wiphy *wiphy)
{
return __ieee80211_resume(wiphy_priv(wiphy));
}
+
+static int ieee80211_set_wow(struct wiphy *wiphy, u32 triggers)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ if (WARN_ON(!(triggers & wiphy->wow.triggers_supported)))
+ return -EOPNOTSUPP;
+
+ return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_WOW);
+}
#else
#define ieee80211_suspend NULL
#define ieee80211_resume NULL
@@ -1362,4 +1372,5 @@ struct cfg80211_ops mac80211_config_ops = {
.join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss,
.set_wiphy_params = ieee80211_set_wiphy_params,
+ .set_wow = ieee80211_set_wow,
};
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 8883d05..0710e4c 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -8,11 +8,15 @@
int __ieee80211_suspend(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct wiphy *wiphy = &local->hw.wiphy;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
unsigned long flags;

+ if (wiphy->wow.triggers_enabled)
+ goto send_stop_request;
+
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);

@@ -62,6 +66,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
}
}

+send_stop_request:
+
/* flush again, in case driver queued work */
flush_workqueue(local->hw.workqueue);

diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 0689a8f..f3d2622 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1029,12 +1029,20 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
+ struct wiphy *wiphy = &local->hw.wiphy;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
unsigned long flags;
int res;

+ /*
+ * We are coming back from suspend and WoW triggers were enabled,
+ * this means we didn't do the usual clean suspend
+ */
+ if (local->suspended && wiphy->wow.triggers_enabled)
+ return 0;
+
/* restart hardware */
if (local->open_count) {
res = drv_start(local);
--
1.6.0.6


2009-05-13 22:27:36

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, 2009-05-13 at 14:00 -0400, Bob Copeland wrote:
> On Wed, May 13, 2009 at 1:20 PM, Luis R. Rodriguez
> <[email protected]> wrote:
> > On Wed, May 13, 2009 at 2:53 AM, Johannes Berg
> > <[email protected]> wrote:
> >> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
> >>> We inform the drivers so they can take appropriate actions during
> >>> suspend for WoW support.
> >>
> >>> -static void ar9170_op_stop(struct ieee80211_hw *hw)
> >>> +static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)
>
> Just curious, why can't the bus suspend method just turn the radio back on?
> You have to set up the wow parameters later anyway.
>
> I think it'd be cleaner if the stack knew about WoW somehow rather than
> doing stuff behind its back in the driver. Then suspend/resume could do
> the right thing (e.g. ops->wow_enable() instead of stop) if the driver was
> WoW-capable.

That's a good point. Somehow I thought this was intimately tied to
suspend. And I think for cfg80211 that makes sense, since cfg80211
doesn't do much for suspend. But for mac80211 you're right in that it
would make sense to
* keep the radio on
* not call stop
* maybe not remove all the sta info structs
* and whatever else may be necessary -- that might even depend on the
wow mode

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-05-13 22:21:37

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, 2009-05-13 at 10:20 -0700, Luis R. Rodriguez wrote:

> > The way you have it, a driver needs to
> > * keep track of the wow config
> > * check whether a stop is due to suspend
> > * apply wow config during stop
> >
> > If you passed the wow config in some struct to _stop() [and NULL if not
> > for wow] then the driver would only need to
> >
> > * check if for wow and apply config during stop
>
> Reason for not passing it is its accessible through the wiphy. All we
> really need from mac80211 is if it changed (some devices may need
> this, and will be more useful for the user pattern handling) and if we
> are stopping due to suspend.
>
> What advantage do we have if we stuff a wow struct instead of having
> the driver read the wiphy wow info?

None on that, but I think it would be a lot simpler for the driver to
check "was I passed wow info" instead of checking "is this for suspend,
if yes, check if there is wow info". In the former case
cfg80211/mac80211 would simply not pass any wow info if wow wasn't
enabled.

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-05-13 09:21:00

by Luis R. Rodriguez

[permalink] [raw]
Subject: [RFC v2 3/5] cfg80211: add WoW support

Signed-off-by: Luis R. Rodriguez <[email protected]>
---
include/linux/nl80211.h | 37 ++++++++++++++
include/net/cfg80211.h | 28 +++++++++++
net/wireless/nl80211.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 186 insertions(+), 0 deletions(-)

diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 58c4ee1..5d9e469 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -240,6 +240,9 @@
* @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
* determined by the network interface.
*
+ * @NL80211_CMD_GET_WOW: get Wake-on-Wireless-LAN settings.
+ * @NL80211_CMD_SET_WOW: set Wake-on-Wireless-LAN settings.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -308,6 +311,9 @@ enum nl80211_commands {
NL80211_CMD_JOIN_IBSS,
NL80211_CMD_LEAVE_IBSS,

+ NL80211_CMD_GET_WOW,
+ NL80211_CMD_SET_WOW,
+
/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
@@ -327,6 +333,8 @@ enum nl80211_commands {
#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+#define NL80211_CMD_GET_WOW NL80211_CMD_GET_WOW
+#define NL80211_CMD_SET_WOW NL80211_CMD_SET_WOW

/**
* enum nl80211_attrs - nl80211 netlink attributes
@@ -499,6 +507,12 @@ enum nl80211_commands {
* this attribute can be used
* with %NL80211_CMD_ASSOCIATE request
*
+ * @NL80211_ATTR_WOW_TRIGGERS_SUPPORTED: the supported WoW triggers
+ * @NL80211_ATTR_WOW_TRIGGERS_ENABLED: used by %NL80211_CMD_SET_WOW to
+ * indicate which WoW triggers should be enabled. This is also
+ * used by %NL80211_CMD_GET_WOW to get the currently enabled WoW
+ * triggers.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -603,6 +617,9 @@ enum nl80211_attrs {

NL80211_ATTR_USE_MFP,

+ NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+ NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
@@ -1196,4 +1213,24 @@ enum nl80211_mfp {
NL80211_MFP_REQUIRED,
};

+/**
+ * enum nl80211_wow_triggers - Wake-on-Wireless-LAN triggers
+ *
+ * NL80211_WOW_TRIGGER_MAGIC_PACKET: a wake signal will be sent to the
+ * devices if a magic packet is received.
+ * NL80211_WOW_TRIGGER_BMISS: WoW signal will be sent to the device when
+ * a beacon has been missed by the associated AP.
+ * NL80211_WOW_TRIGGER_LINK_CHANGE: a wake signal will be sent to
+ * the device if a link change is detected on the device.
+ * NL80211_WOW_TRIGGER_USER_PATTERN: a wake sigal will be sent to the
+ * device if a user configurable pattern is received by
+ * the device.
+ */
+enum nl80211_wow_triggers {
+ NL80211_WOW_TRIGGER_MAGIC_PACKET = 1 << 0,
+ NL80211_WOW_TRIGGER_BMISS = 1 << 1,
+ NL80211_WOW_TRIGGER_LINK_CHANGE = 1 << 2,
+ NL80211_WOW_TRIGGER_PATTERN = 1 << 3,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5906440..5944707 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -765,6 +765,25 @@ enum wiphy_params_flags {
};

/**
+ * struct cfg80211_wow - Wake on Wireless-LAN support info
+ *
+ * This structure defines the WoW triggers supported by the device
+ * and also what specific WoW triggers the user wants enabled. To use
+ * WoW you can use standard ethernet utilities as you would with
+ * Wake-on-LAN.
+ *
+ * @triggers_supported: supported bitmask of WoW triggers.
+ * The flags for this bitmask are %NL80211_WOW_TRIGGER_*.
+ * @triggers_enabled: enabled triggers by the user. Default
+ * is to disable all triggers. The flags for this bitmask
+ * are %NL80211_WOW_TRIGGER_*.
+ */
+struct cfg80211_wow {
+ u32 triggers_supported;
+ u32 triggers_enabled;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -849,6 +868,8 @@ enum wiphy_params_flags {
* @changed bitfield (see &enum wiphy_params_flags) describes which values
* have changed. The actual parameter values are available in
* struct wiphy. If returning an error, no value should be changed.
+ *
+ * @set_wow: used to inform the device which WoW triggers should be enabled.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy);
@@ -940,6 +961,7 @@ struct cfg80211_ops {
int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);

int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
+ int (*set_wow)(struct wiphy *wiphy, u32 triggers);
};

/*
@@ -965,6 +987,11 @@ struct cfg80211_ops {
* channels at a later time. This can be used for devices which do not
* have calibration information gauranteed for frequencies or settings
* outside of its regulatory domain.
+ * @wow: Wake-on-Wireless-LAN configuration struct. The driver should
+ * set the capabilities before registering the wiphy. When we
+ * get a request to enable certain WoW events we will inform
+ * the driver through the set_wow() callback. If this is successfull
+ * we then set the passed triggers as enabled on the wiphy'w wow struct.
* @reg_notifier: the driver's regulatory notification callback
* @regd: the driver's regulatory domain, if one was requested via
* the regulatory_hint() API. This can be used by the driver
@@ -992,6 +1019,7 @@ struct wiphy {
bool strict_regulatory;

enum cfg80211_signal_type signal_type;
+ struct cfg80211_wow wow;

int bss_priv_size;
u8 max_scan_ssids;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0c703b6..25fa6bd 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -123,6 +123,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
[NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
[NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
+
+ [NL80211_ATTR_WOW_TRIGGERS_SUPPORTED] = { .type = NLA_U32 },
+ [NL80211_ATTR_WOW_TRIGGERS_ENABLED] = { .type = NLA_U32 },
};

/* IE validation */
@@ -3270,6 +3273,112 @@ unlock_rtnl:
return err;
}

+static int nl80211_get_wow(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct wiphy *wiphy;
+ int err;
+ void *hdr;
+ struct sk_buff *msg;
+
+ rtnl_lock();
+
+ /* Look up our device */
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto out_rtnl;
+
+ wiphy = &drv->wiphy;
+
+ /* Draw up a netlink message to send back */
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg) {
+ err = -ENOBUFS;
+ goto out;
+ }
+ hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+ NL80211_CMD_GET_WOW);
+ if (!hdr)
+ goto nla_put_failure;
+ NLA_PUT_U32(msg, NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+ wiphy->wow.triggers_supported);
+ NLA_PUT_U32(msg, NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+ wiphy->wow.triggers_enabled);
+
+ genlmsg_end(msg, hdr);
+ err = genlmsg_unicast(msg, info->snd_pid);
+ goto out;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ err = -EMSGSIZE;
+ out:
+ /* Cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
+static int nl80211_set_wow(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct wiphy *wiphy;
+ u32 triggers_requested;
+
+ if (!info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED])
+ return -EINVAL;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto out_rtnl;
+
+ if (!drv->ops->set_wow) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+
+ if (!wiphy->wow.triggers_supported) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ triggers_requested =
+ nla_get_u32(info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED]);
+
+ if (!(wiphy->wow.triggers_supported & triggers_requested)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Apply changes */
+ err = drv->ops->set_wow(&drv->wiphy, triggers_requested);
+
+ if (err)
+ goto out;
+
+ wiphy->wow.triggers_enabled = triggers_requested;
+
+ out:
+ /* cleanup */
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -3483,6 +3592,18 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_GET_WOW,
+ .doit = nl80211_get_wow,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_WOW,
+ .doit = nl80211_set_wow,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
.name = "mlme",
--
1.6.0.6


2009-05-13 18:13:24

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 11:00:45AM -0700, Bob Copeland wrote:
> On Wed, May 13, 2009 at 1:20 PM, Luis R. Rodriguez
> <[email protected]> wrote:
> > On Wed, May 13, 2009 at 2:53 AM, Johannes Berg
> > <[email protected]> wrote:
> >> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
> >>> We inform the drivers so they can take appropriate actions during
> >>> suspend for WoW support.
> >>
> >>> -static void ar9170_op_stop(struct ieee80211_hw *hw)
> >>> +static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)
>
> Just curious, why can't the bus suspend method just turn the radio back on?
> You have to set up the wow parameters later anyway.

That's possible too.

> I think it'd be cleaner if the stack knew about WoW somehow rather than
> doing stuff behind its back in the driver. Then suspend/resume could do
> the right thing (e.g. ops->wow_enable() instead of stop) if the driver was
> WoW-capable.

Agreed, however ath9k is the only card I know how WoW works so far. If
other devices would follow this logic then I think it makes sense to use
something like a cfg callback for wow_enable() -- but then again this is
only done for ath9k for suspend so doing it at suspend saves us a callback.

Thoughts?

Luis

2009-05-14 22:02:01

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 4:25 PM, Luis R. Rodriguez
<[email protected]> wrote:
> On Wed, May 13, 2009 at 4:13 PM, Johannes Berg
> <[email protected]> wrote:
>> On Wed, 2009-05-13 at 16:08 -0700, Luis R. Rodriguez wrote:
>
>>> >  * maybe not remove all the sta info structs
>>>
>>> Which ones? In my tests I'm just not removing any.
>>
>> I'm talking about sta_notify() not code :)
>
> Oh I wasn't talking about code either but about early exist stuff I
> was doing upon __ieee80211_suspend().
>
>>> >  * and whatever else may be necessary -- that might even depend on the
>>> >   wow mode
>>>
>>> So lets start with a basic goal -- magic packet. If you think about it
>>> though if you disassociate you won't get this magic packet
>>
>> Actually I think you still can or something, it's very strange. Does
>> anyone have a WoW documentation/spec?
>
> Good question. I'll poke internally see what I can get.

I've documented everything generic I learned about WoW here:

http://wireless.kernel.org/en/users/Documentation/WoW

At least for Atheros devices it seems we must not disassociate the
device as we just have hardware and we need to keep it in that state.

>>> so I guess
>>> that's also why we have link-change trigger (or called disassoc
>>> trigger on some other hardware?).
>>
>> That could make some sense, though do you want to wake up, reassociate
>> (if possible) and go to sleep again? That would take some
>> synchronisation across layers...
>
> I hope not, but anyway if that is done someone can use custom scripts
> to go back to sleep, or do we even want to bother passing this up to
> the supplicant as an event upon resume? Hmm -- yeah Jouni, would the
> supplicant benefit from having WoW events sent back to it after
> resume, or even before suspend?
>
>>>  Anyway out of these link change and
>>> magic packet seem like a reasonable goal to get working first. For
>>> that besides the above I'm not sure what else is required.
>>>
>>> I'm building this stuff now on a box with a card that is supposed to
>>> have WoW working so I'll know for sure if we are missing something
>>> soon.
>>
>> Cool.
>>
>> Anyway I think Bob is right, we may not need to have anything added to
>> ->stop() and instead do some wow config, or something. Too tired to
>> think about it I think.
>
> I added a WOW hw config, if we use that then we need a way for the
> device to get the wow config stuff. I was thinking we'd use the
> wiphy->wow for that but it seems you rather we just pass the settings
> completely up once and let the driver worry how to stuff this in the
> driver somewhere.

As for mac80211 it seems we should not call drv stop and also not
disassociate when wow is enabled. We could take care of this by adding
a wow_enable cfg callback as Bob had suggested and let mac80211 update
the ieee80211_hw.wow_enabled triggers passed. We'll need these enabled
triggers in mac80211 to be able to decide things we should do while
WoW is enabled (during suspend && wow_enabled).

If we stuff wow_enabled_triggers into into ieee80211_hw we don't
really need a mac80211 hw config change notice to drivers. At least
ath9k doesn't need it. If we want to hide this from drivers and want
them to keep track of this internally we could stuff the
wow_triggers_enabled into the local and then use the config to let
drivers store it internally themselves as well. But that just seems
more code and not sure what the advantage is.

During its driver's suspend callback drivers can do what it thinks is
best. This means we don't have to bother with the mac80211 stop
callback changes at all and just let mac80211 handle the upper layer
stuff and the driver just handle the suspend / wow stuff.

Sound OK?

Luis

2009-05-13 09:53:40

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
> We inform the drivers so they can take appropriate actions during
> suspend for WoW support.

> -static void ar9170_op_stop(struct ieee80211_hw *hw)
> +static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)

Why just a 'suspend' bool? Why not pass the WoW information in here,
instead of adding a new config and stuff for it? WoW information is only
relevant during suspend.

The way you have it, a driver needs to
* keep track of the wow config
* check whether a stop is due to suspend
* apply wow config during stop

If you passed the wow config in some struct to _stop() [and NULL if not
for wow] then the driver would only need to

* check if for wow and apply config during stop

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-05-13 18:07:15

by Bob Copeland

[permalink] [raw]
Subject: Re: [RFC v2 2/5] mac80211: inform devices when we are suspending on the stop callback

On Wed, May 13, 2009 at 1:20 PM, Luis R. Rodriguez
<[email protected]> wrote:
> On Wed, May 13, 2009 at 2:53 AM, Johannes Berg
> <[email protected]> wrote:
>> On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:
>>> We inform the drivers so they can take appropriate actions during
>>> suspend for WoW support.
>>
>>> -static void ar9170_op_stop(struct ieee80211_hw *hw)
>>> +static void ar9170_op_stop(struct ieee80211_hw *hw, bool suspend)

Just curious, why can't the bus suspend method just turn the radio back on?
You have to set up the wow parameters later anyway.

I think it'd be cleaner if the stack knew about WoW somehow rather than
doing stuff behind its back in the driver. Then suspend/resume could do
the right thing (e.g. ops->wow_enable() instead of stop) if the driver was
WoW-capable.

--
Bob Copeland %% http://www.bobcopeland.com

2009-05-13 22:31:15

by Luis R. Rodriguez

[permalink] [raw]
Subject: Re: [RFC v2 3/5] cfg80211: add WoW support

On Wed, May 13, 2009 at 3:22 PM, Johannes Berg
<[email protected]> wrote:
> On Wed, 2009-05-13 at 11:14 -0700, Luis R. Rodriguez wrote:
>
>> > > +enum nl80211_wow_triggers {
>> > > + NL80211_WOW_TRIGGER_MAGIC_PACKET =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D=
1 << 0,
>> > > + NL80211_WOW_TRIGGER_BMISS =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=
=A0 =C2=A0 =3D 1 << 1,
>> > > + NL80211_WOW_TRIGGER_LINK_CHANGE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D=
1 << 2,
>> > > + NL80211_WOW_TRIGGER_PATTERN =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=
=C2=A0 =3D 1 << 3,
>> > > +};
>> >
>> > Seems like you need to have a way to set the pattern and magic pac=
ket?
>>
>> Yeah TDB -- the code for that is a bit complex and I don't understan=
d how
>> things are being broken down yet. We could just not have the cababil=
ity
>> present until that's properly figured out.
>
> Yeah I think that would be preferable, maybe even not define the bits
> until we figure out what else we need. It might not actually make sen=
se
> to use bits here, for example if you need a magic packet to wake up i=
t
> might make sense to have just a magic packet netlink attribute that, =
if
> userspace passes it, defines that magic packet with that packet shoul=
d
> be used, instead of requiring that the user sets both the bit and giv=
es
> the packet. etc.

True -- the bit is useful for capability though, not for setting.

Luis

2009-05-13 09:58:56

by Johannes Berg

[permalink] [raw]
Subject: Re: [RFC v2 3/5] cfg80211: add WoW support

On Wed, 2009-05-13 at 05:20 -0400, Luis R. Rodriguez wrote:

> /**
> + * struct cfg80211_wow - Wake on Wireless-LAN support info
> + *
> + * This structure defines the WoW triggers supported by the device
> + * and also what specific WoW triggers the user wants enabled. To use
> + * WoW you can use standard ethernet utilities as you would with
> + * Wake-on-LAN.
> + *
> + * @triggers_supported: supported bitmask of WoW triggers.
> + * The flags for this bitmask are %NL80211_WOW_TRIGGER_*.
> + * @triggers_enabled: enabled triggers by the user. Default
> + * is to disable all triggers. The flags for this bitmask
> + * are %NL80211_WOW_TRIGGER_*.
> + */
> +struct cfg80211_wow {
> + u32 triggers_supported;
> + u32 triggers_enabled;
> +};
> +
> +/**
> * struct cfg80211_ops - backend description for wireless configuration
> *
> * This struct is registered by fullmac card drivers and/or wireless stacks
> @@ -849,6 +868,8 @@ enum wiphy_params_flags {
> * @changed bitfield (see &enum wiphy_params_flags) describes which values
> * have changed. The actual parameter values are available in
> * struct wiphy. If returning an error, no value should be changed.
> + *
> + * @set_wow: used to inform the device which WoW triggers should be enabled.
> */
> struct cfg80211_ops {
> int (*suspend)(struct wiphy *wiphy);
> @@ -940,6 +961,7 @@ struct cfg80211_ops {
> int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev);
>
> int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed);
> + int (*set_wow)(struct wiphy *wiphy, u32 triggers);
> };
>
> /*
> @@ -965,6 +987,11 @@ struct cfg80211_ops {
> * channels at a later time. This can be used for devices which do not
> * have calibration information gauranteed for frequencies or settings
> * outside of its regulatory domain.
> + * @wow: Wake-on-Wireless-LAN configuration struct. The driver should
> + * set the capabilities before registering the wiphy. When we
> + * get a request to enable certain WoW events we will inform
> + * the driver through the set_wow() callback. If this is successfull
> + * we then set the passed triggers as enabled on the wiphy'w wow struct.

I'm not sure that makes sense. Why keep track of both the enabled and
the possible triggers, _and_ have a set_wow callback? The latter might
make sense to verify some of the information, but I think we shouldn't
keep track of the enabled triggers here but rather do that in the driver
then.

Alternatively, how about we push the stuff I suggested for mac80211 all
the way through to cfg80211? The cfg80211 suspend() method could pass
through the wow parameters.

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part