* [PATCH 0/5] wireless: add wow support
@ 2009-07-15 0:21 Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 1/5] ath9k: do not stop the queues in driver stop Luis R. Rodriguez
` (4 more replies)
0 siblings, 5 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw)
To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
This series adds Wake-on-Wireless support for wireless with
initial support for ath9k. No event reporting is done yet,
we'll leave that for later once we figure out a unified
method for all devices (including non-wireless).
If you are not familiar with WoW you are encouraged to read:
http://wireless.kernel.org/en/users/Documentation/WoW
Please note not all laptops/desktops will have support for
allowing WoW. You will need proper BIOS support, also not
all cards have it enabled.
Luis R. Rodriguez (5):
ath9k: do not stop the queues in driver stop
adm8211: remove uneeded code during suspend/resume
cfg80211: add WoW support
mac80211: add WoW support
ath9k: Add Wake-on-Wireless-LAN support
drivers/net/wireless/adm8211.c | 19 +-
drivers/net/wireless/at76c50x-usb.c | 3 +-
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 | 18 +
drivers/net/wireless/ath/ath9k/debug.c | 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 | 27 ++-
drivers/net/wireless/ath/ath9k/pci.c | 102 ++++++
drivers/net/wireless/ath/ath9k/reg.h | 154 ++++++++
drivers/net/wireless/ath/ath9k/wow.c | 511 +++++++++++++++++++++++++++
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/main.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/wl1251_main.c | 2 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 2 +-
include/linux/nl80211.h | 46 +++
include/net/cfg80211.h | 26 ++-
include/net/mac80211.h | 17 +-
net/mac80211/cfg.c | 5 +-
net/mac80211/debugfs.c | 2 +-
net/mac80211/driver-ops.h | 6 +-
net/mac80211/driver-trace.h | 6 +-
net/mac80211/ieee80211_i.h | 12 +-
net/mac80211/iface.c | 6 +-
net/mac80211/pm.c | 31 ++-
net/mac80211/util.c | 14 +
net/wireless/core.h | 3 +
net/wireless/nl80211.c | 116 ++++++
net/wireless/sysfs.c | 2 +-
42 files changed, 1172 insertions(+), 64 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath9k/wow.c
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/5] ath9k: do not stop the queues in driver stop
2009-07-15 0:21 [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
@ 2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 2/5] adm8211: remove uneeded code during suspend/resume Luis R. Rodriguez
` (3 subsequent siblings)
4 siblings, 0 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw)
To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
mac80211 will have disabled the queues for us when
needed.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/ath/ath9k/main.c | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index d14f8c9..907434a 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2101,8 +2101,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
mutex_lock(&sc->mutex);
- ieee80211_stop_queues(hw);
-
if (ath9k_wiphy_started(sc)) {
mutex_unlock(&sc->mutex);
return; /* another wiphy still in use */
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 2/5] adm8211: remove uneeded code during suspend/resume
2009-07-15 0:21 [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 1/5] ath9k: do not stop the queues in driver stop Luis R. Rodriguez
@ 2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 3/5] cfg80211: add WoW support Luis R. Rodriguez
` (2 subsequent siblings)
4 siblings, 0 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw)
To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
mac80211 drivers do not need to stop the software queues
or call their own stop() callback upon suspend as we do it
for drivers. Equally drivers don't have to call their own
start() or start the queues as mac80211 will do it for us.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/adm8211.c | 17 -----------------
1 files changed, 0 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index ecc9383..5695911 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1964,14 +1964,6 @@ static void __devexit adm8211_remove(struct pci_dev *pdev)
#ifdef CONFIG_PM
static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
{
- struct ieee80211_hw *dev = pci_get_drvdata(pdev);
- struct adm8211_priv *priv = dev->priv;
-
- if (priv->mode != NL80211_IFTYPE_UNSPECIFIED) {
- ieee80211_stop_queues(dev);
- adm8211_stop(dev);
- }
-
pci_save_state(pdev);
pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
@@ -1979,17 +1971,8 @@ static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state)
static int adm8211_resume(struct pci_dev *pdev)
{
- struct ieee80211_hw *dev = pci_get_drvdata(pdev);
- struct adm8211_priv *priv = dev->priv;
-
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
-
- if (priv->mode != NL80211_IFTYPE_UNSPECIFIED) {
- adm8211_start(dev);
- ieee80211_wake_queues(dev);
- }
-
return 0;
}
#endif /* CONFIG_PM */
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/5] cfg80211: add WoW support
2009-07-15 0:21 [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 1/5] ath9k: do not stop the queues in driver stop Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 2/5] adm8211: remove uneeded code during suspend/resume Luis R. Rodriguez
@ 2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 11:45 ` Johannes Berg
2009-07-15 0:21 ` [PATCH 4/5] mac80211: " Luis R. Rodriguez
2009-07-15 0:22 ` [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support Luis R. Rodriguez
4 siblings, 1 reply; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw)
To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
We support a set and get for WoW parameters. We support
setting of 3 type of triggers:
* Magic Packet
* Beacon miss
* Link change (no longer associated)
Not yet supported:
* User pattern match
The user pattern match will need a parser as patterns
can be customized. It should be noted WoW will require
suspend to S3 Hot (power cable plugged), and proper
BIOS support to receive PCI PMEs and kick the box on.
For USB this may be easier and no BIOS intervention should
be required.
Drivers which support WoW must set their supported hardware/driver
triggers on the wiphy's cfg80211 wow_triggers_supported. cfg80211
drivers will be informed of the enabled wow triggers through the
suspend() cfg80211 callback. By default we disable all triggers.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
include/linux/nl80211.h | 46 +++++++++++++++++++
include/net/cfg80211.h | 26 ++++++++++-
net/wireless/core.h | 3 +
net/wireless/nl80211.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/sysfs.c | 2 +-
5 files changed, 190 insertions(+), 3 deletions(-)
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 962e223..0c869ca 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -262,6 +262,18 @@
* reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
* %NL80211_ATTR_REASON_CODE attributes are used.
*
+ * @NL80211_CMD_GET_WOW: get Wake-on-Wireless-LAN (WoW) settings.
+ * @NL80211_CMD_SET_WOW: set Wake-on-Wireless-LAN (Wow) settings. Wake on
+ * wireless makes use of standard Wake-on-LAN (WoL) frames, you receive
+ * a WoW frame when your AP sends you a regular WOL frame. The difference
+ * difference WoL is you need to be associated to an AP in order to
+ * receive WoW frames, so additional triggers are available for a wakeup.
+ * A driver capable of WoW should initialize the wiphy with its supported
+ * WoW triggers. Upon suspend cfg80211 will inform the driver of the user
+ * enabled triggers. By default no WoW triggers are enabled.
+ * For more information see:
+ * http://wireless.kernel.org/en/users/Documentation/WoW
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -336,6 +348,9 @@ enum nl80211_commands {
NL80211_CMD_ROAM,
NL80211_CMD_DISCONNECT,
+ NL80211_CMD_GET_WOW,
+ NL80211_CMD_SET_WOW,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -355,6 +370,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
@@ -573,6 +590,12 @@ enum nl80211_commands {
* and join_ibss(), key information is in a nested attribute each
* with %NL80211_KEY_* sub-attributes
*
+ * @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
*/
@@ -701,6 +724,9 @@ enum nl80211_attrs {
NL80211_ATTR_KEY,
NL80211_ATTR_KEYS,
+ NL80211_ATTR_WOW_TRIGGERS_SUPPORTED,
+ NL80211_ATTR_WOW_TRIGGERS_ENABLED,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -1372,4 +1398,24 @@ enum nl80211_key_attributes {
NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
};
+/**
+ * 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 signal 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 a981ca8..d45fcec 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -828,6 +828,21 @@ struct cfg80211_bitrate_mask {
u32 maxrate; /* in kbps, 0 == no limit */
};
+/*
+ * struct cfg80211_wow - Wake on Wireless-LAN support info
+ *
+ * This structure defines the WoW triggers enabled and set for the device.
+ * For now we only carry the supported tiggers but this is expected to grow
+ * once we add support for user patterns.
+ *
+ * @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_enabled;
+};
+
/**
* struct cfg80211_ops - backend description for wireless configuration
*
@@ -841,7 +856,8 @@ struct cfg80211_bitrate_mask {
* wireless extensions but this is subject to reevaluation as soon as this
* code is used more widely and we have a first user without wext.
*
- * @suspend: wiphy device needs to be suspended
+ * @suspend: wiphy device needs to be suspended. We pass the struct cfg80211_wow
+ * so the device enables the appropriate triggers during suspend.
* @resume: wiphy device needs to be resumed
*
* @add_virtual_intf: create a new virtual interface with the given name,
@@ -931,7 +947,7 @@ struct cfg80211_bitrate_mask {
* @testmode_cmd: run a test mode command
*/
struct cfg80211_ops {
- int (*suspend)(struct wiphy *wiphy);
+ int (*suspend)(struct wiphy *wiphy, struct cfg80211_wow *wow);
int (*resume)(struct wiphy *wiphy);
int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -1088,6 +1104,10 @@ struct cfg80211_ops {
* @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
* -1 = fragmentation disabled, only odd values >= 256 used
* @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled
+ * @wow_triggers_supported: supported bitmask of Wake-on-Wireless triggers.
+ * The flags for this bitmask are %NL80211_WOW_TRIGGER_*. The driver
+ * should set the capabilities before registering the wiphy. WoW triggers
+ * which should be used are passed to the driver upon suspend.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
@@ -1115,6 +1135,8 @@ struct wiphy {
u32 frag_threshold;
u32 rts_threshold;
+ u32 wow_triggers_supported;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 2ec8ddb..1d250cd 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -66,6 +66,9 @@ struct cfg80211_registered_device {
struct work_struct conn_work;
struct work_struct event_work;
+ /* Used to keep track of user configurable triggers */
+ struct cfg80211_wow wow;
+
#ifdef CONFIG_CFG80211_DEBUGFS
/* Debugfs entries */
struct wiphy_debugfsdentries {
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6dad3e7..50de866 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -133,6 +133,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
+
+ [NL80211_ATTR_WOW_TRIGGERS_SUPPORTED] = { .type = NLA_U32 },
+ [NL80211_ATTR_WOW_TRIGGERS_ENABLED] = { .type = NLA_U32 },
};
/* policy for the attributes */
@@ -4023,6 +4026,109 @@ unlock_rtnl:
return err;
}
+static int nl80211_get_wow(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ struct net_device *dev;
+ struct wiphy *wiphy;
+ int err;
+ void *hdr;
+ struct sk_buff *msg;
+
+ rtnl_lock();
+
+ /* Look up our device */
+ err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+ if (err)
+ goto out_rtnl;
+
+ wiphy = &rdev->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,
+ rdev->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_unlock_rdev(rdev);
+ 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 *rdev;
+ struct net_device *dev;
+ struct wiphy *wiphy;
+ u32 triggers_requested;
+
+ if (!info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED])
+ return -EINVAL;
+
+ rtnl_lock();
+
+ err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+ if (err)
+ goto out_rtnl;
+
+ wiphy = &rdev->wiphy;
+
+ if (!wiphy->wow_triggers_supported) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ triggers_requested =
+ nla_get_u32(info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED]);
+
+ /* Needs a pattern attribute implemented and passed to the driver */
+ if (triggers_requested & NL80211_WOW_TRIGGER_PATTERN)
+ return -EOPNOTSUPP;
+
+ if (!(wiphy->wow_triggers_supported & triggers_requested)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /*
+ * Apply changes. This information gets passed to the
+ * drivers during suspend.
+ */
+ rdev->wow.triggers_enabled = triggers_requested;
+
+ out:
+ /* cleanup */
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -4253,6 +4359,16 @@ static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_DISCONNECT,
.doit = nl80211_disconnect,
+ },
+ {
+ .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,
},
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index efe3c5c..8b60a5b 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -64,7 +64,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
if (rdev->ops->suspend) {
rtnl_lock();
- ret = rdev->ops->suspend(&rdev->wiphy);
+ ret = rdev->ops->suspend(&rdev->wiphy, &rdev->wow);
rtnl_unlock();
}
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 4/5] mac80211: add WoW support
2009-07-15 0:21 [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
` (2 preceding siblings ...)
2009-07-15 0:21 ` [PATCH 3/5] cfg80211: add WoW support Luis R. Rodriguez
@ 2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 11:57 ` Johannes Berg
2009-07-15 0:22 ` [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support Luis R. Rodriguez
4 siblings, 1 reply; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw)
To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
If we're associated and have not already gone into PS mode
we ensure send a nullfunc frame before going to suspend. Upon
resume we ensure we restore PS mode if it was enabled and inform
the driver accordingly, also notify the AP we're awake.
WoW requires the radio to be left on during suspend, to do this
we inform drivers of the requested wow triggers on the mac80211
stop callback.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/adm8211.c | 2 +-
drivers/net/wireless/at76c50x-usb.c | 3 +-
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/main.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/wl1251_main.c | 2 +-
drivers/net/wireless/zd1211rw/zd_mac.c | 2 +-
include/net/mac80211.h | 17 +++++++++-----
net/mac80211/cfg.c | 5 ++-
net/mac80211/debugfs.c | 2 +-
net/mac80211/driver-ops.h | 6 ++--
net/mac80211/driver-trace.h | 6 +++-
net/mac80211/ieee80211_i.h | 12 ++++++++-
net/mac80211/iface.c | 6 ++--
net/mac80211/pm.c | 31 +++++++++++++++++++++++++-
net/mac80211/util.c | 14 ++++++++++++
28 files changed, 99 insertions(+), 41 deletions(-)
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c
index 5695911..776a715 100644
--- a/drivers/net/wireless/adm8211.c
+++ b/drivers/net/wireless/adm8211.c
@@ -1547,7 +1547,7 @@ fail:
return retval;
}
-static void adm8211_stop(struct ieee80211_hw *dev)
+static void adm8211_stop(struct ieee80211_hw *dev, struct cfg80211_wow *wow)
{
struct adm8211_priv *priv = dev->priv;
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index 13303fa..8357fef 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1767,7 +1767,8 @@ error:
return 0;
}
-static void at76_mac80211_stop(struct ieee80211_hw *hw)
+static void at76_mac80211_stop(struct ieee80211_hw *hw,
+ struct cfg80211_wow *wow)
{
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 51753ed..2c2f002 100644
--- a/drivers/net/wireless/ath/ar9170/main.c
+++ b/drivers/net/wireless/ath/ar9170/main.c
@@ -1132,7 +1132,7 @@ out:
return err;
}
-static void ar9170_op_stop(struct ieee80211_hw *hw)
+static void ar9170_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
struct ar9170 *ar = hw->priv;
unsigned int i;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 20ba6fa..0bd53e8 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -223,7 +223,7 @@ static int ath5k_tx_queue(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, struct cfg80211_wow *wow);
static int ath5k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf);
static void ath5k_remove_interface(struct ieee80211_hw *hw,
@@ -2735,7 +2735,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, struct cfg80211_wow *wow)
{
ath5k_stop_hw(hw->priv);
}
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 907434a..f40f2cf 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -2087,7 +2087,7 @@ exit:
return 0;
}
-static void ath9k_stop(struct ieee80211_hw *hw)
+static void ath9k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
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 e71c8d9..f0ca9d0 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -4368,7 +4368,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, struct cfg80211_wow *wow)
{
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 c4973c1..9235578 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -3469,7 +3469,7 @@ out_mutex_unlock:
return err;
}
-static void b43legacy_op_stop(struct ieee80211_hw *hw)
+static void b43legacy_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
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 13279b1..fd2c146 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -2137,7 +2137,7 @@ out:
return 0;
}
-static void iwl_mac_stop(struct ieee80211_hw *hw)
+static void iwl_mac_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
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 a05a039..83cc25c 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -3126,7 +3126,7 @@ out_release_irq:
return ret;
}
-static void iwl3945_mac_stop(struct ieee80211_hw *hw)
+static void iwl3945_mac_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
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 4872345..8557ca4 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, struct cfg80211_wow *wow)
{
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 2e2af65..516a31c 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -488,7 +488,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, struct cfg80211_wow *wow)
{
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 b9eded8..1261f8d 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -2917,7 +2917,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, struct cfg80211_wow *wow)
{
int rc;
struct mwl8k_stop_worker *worker;
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c
index c9a0545..43b5940 100644
--- a/drivers/net/wireless/p54/main.c
+++ b/drivers/net/wireless/p54/main.c
@@ -190,7 +190,7 @@ out:
return err;
}
-static void p54_stop(struct ieee80211_hw *dev)
+static void p54_stop(struct ieee80211_hw *dev, struct cfg80211_wow *wow)
{
struct p54_common *priv = dev->priv;
int i;
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 71f37cb..e2701fa 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -953,7 +953,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, struct cfg80211_wow *wow);
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 b7e0ddd..4522c96 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -179,7 +179,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, struct cfg80211_wow *wow)
{
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 09f46ab..807605c 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -628,7 +628,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, struct cfg80211_wow *wow)
{
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 c9b9dbe..fd1e8f8 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -989,7 +989,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, struct cfg80211_wow *wow)
{
struct rtl8187_priv *priv = dev->priv;
struct sk_buff *skb;
diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c
index cf5e054..a87a25a 100644
--- a/drivers/net/wireless/wl12xx/wl1251_main.c
+++ b/drivers/net/wireless/wl12xx/wl1251_main.c
@@ -376,7 +376,7 @@ out:
return ret;
}
-static void wl1251_op_stop(struct ieee80211_hw *hw)
+static void wl1251_op_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
struct wl1251 *wl = hw->priv;
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 9600b72..70dcf01 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, struct cfg80211_wow *wow)
{
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 7dd67a1..004e6b3 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -545,6 +545,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),
@@ -555,6 +556,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
@@ -1294,11 +1296,14 @@ 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.
- * Must be implemented.
+ * 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. In order to
+ * process WoW triggers the radio must be left on and the driver
+ * must enable the triggers in hardware. This callback may be
+ * called right after add_interface if that rejects an interface.
+ * Must be implemented. WoW triggers which should be enabled prior
+ * suspend are passed in the callback.
*
* @add_interface: Called when a netdevice attached to the hardware is
* enabled. Because it is not called for monitor mode devices, @start
@@ -1425,7 +1430,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, struct cfg80211_wow *wow);
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/cfg.c b/net/mac80211/cfg.c
index 52928ad..afa6476 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1138,15 +1138,16 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
}
#ifdef CONFIG_PM
-static int ieee80211_suspend(struct wiphy *wiphy)
+static int ieee80211_suspend(struct wiphy *wiphy, struct cfg80211_wow *wow)
{
- return __ieee80211_suspend(wiphy_priv(wiphy));
+ return __ieee80211_suspend(wiphy_priv(wiphy), wow);
}
static int ieee80211_resume(struct wiphy *wiphy)
{
return __ieee80211_resume(wiphy_priv(wiphy));
}
+
#else
#define ieee80211_suspend NULL
#define ieee80211_resume NULL
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 96991b6..8985c40 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -115,7 +115,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf,
struct ieee80211_local *local = file->private_data;
rtnl_lock();
- __ieee80211_suspend(&local->hw);
+ __ieee80211_suspend(&local->hw, NULL);
__ieee80211_resume(&local->hw);
rtnl_unlock();
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 4100c36..f12488f 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -17,10 +17,10 @@ static inline int drv_start(struct ieee80211_local *local)
return ret;
}
-static inline void drv_stop(struct ieee80211_local *local)
+static inline void drv_stop(struct ieee80211_local *local, struct cfg80211_wow *wow)
{
- local->ops->stop(&local->hw);
- trace_drv_stop(local);
+ local->ops->stop(&local->hw, wow);
+ trace_drv_stop(local, wow);
}
static inline int drv_add_interface(struct ieee80211_local *local,
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 48c93d1..f83f913 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -51,16 +51,18 @@ TRACE_EVENT(drv_start,
);
TRACE_EVENT(drv_stop,
- TP_PROTO(struct ieee80211_local *local),
+ TP_PROTO(struct ieee80211_local *local, struct cfg80211_wow *wow),
- TP_ARGS(local),
+ TP_ARGS(local, wow),
TP_STRUCT__entry(
LOCAL_ENTRY
+ __field(u32, triggers_enabled)
),
TP_fast_assign(
LOCAL_ASSIGN;
+ __entry->triggers_enabled = wow->triggers_enabled;
),
TP_printk(
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 6a01771..2ebf56d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -612,6 +612,14 @@ struct ieee80211_local {
*/
bool quiescing;
+ /*
+ * This will be true if we had enabled PS before going to suspend.
+ * We use this to help resume restore the PS state to what it was.
+ * Note that this only makes sense if we're associated and are a
+ * station.
+ */
+ bool ps_before_suspend;
+
int tx_headroom; /* required headroom for hardware/radiotap */
/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -1035,14 +1043,14 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
int ieee80211_reconfig(struct ieee80211_local *local);
#ifdef CONFIG_PM
-int __ieee80211_suspend(struct ieee80211_hw *hw);
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wow *wow);
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
{
return ieee80211_reconfig(hw_to_local(hw));
}
#else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
+static inline int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
return 0;
}
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index cadb0f6..b5e1c76 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -195,7 +195,7 @@ static int ieee80211_open(struct net_device *dev)
*/
if (!is_valid_ether_addr(dev->dev_addr)) {
if (!local->open_count)
- drv_stop(local);
+ drv_stop(local, NULL);
return -EADDRNOTAVAIL;
}
@@ -321,7 +321,7 @@ static int ieee80211_open(struct net_device *dev)
drv_remove_interface(local, &conf);
err_stop:
if (!local->open_count)
- drv_stop(local);
+ drv_stop(local, NULL);
err_del_bss:
sdata->bss = NULL;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -532,7 +532,7 @@ static int ieee80211_stop(struct net_device *dev)
ieee80211_recalc_ps(local, -1);
if (local->open_count == 0) {
- drv_stop(local);
+ drv_stop(local, NULL);
ieee80211_led_radio(local, false);
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 7a549f9..d1918ad 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -6,7 +6,7 @@
#include "driver-ops.h"
#include "led.h"
-int __ieee80211_suspend(struct ieee80211_hw *hw)
+int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
@@ -16,6 +16,33 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
ieee80211_scan_cancel(local);
+ local->ps_before_suspend = !!(local->hw.conf.flags & IEEE80211_CONF_PS);
+
+ if (wow->triggers_enabled) {
+ if (!local->ps_before_suspend) {
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ struct ieee80211_if_managed *ifmgd;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ continue;
+
+ ifmgd = &sdata->u.mgd;
+ if (!ifmgd->associated)
+ continue;
+
+ if (!local->ps_before_suspend)
+ ieee80211_send_nullfunc(local, sdata, 1);
+ }
+
+ }
+
ieee80211_stop_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
@@ -61,7 +88,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
/* stop hardware - this must stop RX */
if (local->open_count) {
ieee80211_led_radio(local, false);
- drv_stop(local);
+ drv_stop(local, wow);
}
/* remove STAs */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 7fc5584..c6985ce 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -970,6 +970,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
*/
local->suspended = false;
+ if (from_suspend && local->ps_before_suspend)
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+
/* restart hardware */
if (local->open_count) {
res = drv_start(local);
@@ -1072,6 +1075,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)
return 0;
#ifdef CONFIG_PM
+ if (!local->ps_before_suspend) {
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ struct ieee80211_if_managed *ifmgd;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ continue;
+ ifmgd = &sdata->u.mgd;
+ if (ifmgd->associated)
+ ieee80211_send_nullfunc(local, sdata, 0);
+ }
+ }
+
local->suspended = false;
list_for_each_entry(sdata, &local->interfaces, list) {
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support
2009-07-15 0:21 [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
` (3 preceding siblings ...)
2009-07-15 0:21 ` [PATCH 4/5] mac80211: " Luis R. Rodriguez
@ 2009-07-15 0:22 ` Luis R. Rodriguez
4 siblings, 0 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 0:22 UTC (permalink / raw)
To: linville, johannes; +Cc: linux-wireless, ath9k-devel, Luis R. Rodriguez
This adds WoW suppport for ath9k. Note that not all cards
have WoW support available; you also need BIOS support to
trigger a wake from the device's PCI PME. We enable only
Magic Packet triggers for now, note that if you disconnect
you won't get a wake up. Beacon miss and link change triggers
need more testing before being enabled.
Tested on AR9280 with Magic Packet.
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
---
drivers/net/wireless/ath/ath9k/Makefile | 1 +
drivers/net/wireless/ath/ath9k/ath9k.h | 18 +
drivers/net/wireless/ath/ath9k/debug.c | 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 | 102 ++++++
drivers/net/wireless/ath/ath9k/reg.h | 154 +++++++++
drivers/net/wireless/ath/ath9k/wow.c | 511 +++++++++++++++++++++++++++++
10 files changed, 883 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 1576812..2723385 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -604,6 +604,12 @@ struct ath_softc {
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;
+ u32 wow_triggers_enabled;
+#endif
#ifdef CONFIG_ATH9K_DEBUG
struct ath9k_debug debug;
#endif
@@ -612,6 +618,18 @@ struct ath_softc {
struct delayed_work tx_complete_work;
};
+#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 9f99f00..8705c35 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -486,7 +486,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/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index b9d1a13..3d98f8d 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 9a4570d..6126b99 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -520,6 +520,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;
@@ -535,6 +540,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 */
@@ -613,4 +622,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 */
+void 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 f67a2a9..52e1743 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 Revsion 10*/
static const u_int32_t ar9285Modes_9285[][6] = {
{ 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 },
@@ -4849,3 +4865,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 f40f2cf..84c0fed 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -596,6 +596,20 @@ 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_ANY | ATH_DBG_INTERRUPT),
+ "during WoW we got a BMISS\n");
+ sc->wow_got_bmiss_intr = true;
+ sc->wow_sleep_proc_intr = false;
+ }
+#else
+ /* BMISS not enabled on ath9k unless WoW is enabled */
+ DPRINTF(sc, ATH_DBG_INTERRUPT,
+ "spurious unattended beacon miss interrupt\n");
+#endif
+ }
if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
if (status & ATH9K_INT_TIM_TIMER) {
/* Clear RxAbort bit so that we can
@@ -2094,6 +2108,15 @@ static void ath9k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow)
aphy->state = ATH_WIPHY_INACTIVE;
+#ifdef CONFIG_PM
+ if (wow && wow->triggers_enabled) {
+ sc->wow_triggers_enabled = 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 170c5b3..4d5865c 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -142,6 +142,9 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+ 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");
@@ -192,6 +195,14 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sc->irq = pdev->irq;
+ /* WoW is for PCI for ath9k */
+
+ /*
+ * Bmiss and link change triggers not yet fully tested, add them
+ * after properly testing and confirming behaviour.
+ */
+ hw->wiphy->wow_triggers_supported = NL80211_WOW_TRIGGER_MAGIC_PACKET;
+
ah = sc->sc_ah;
printk(KERN_INFO
"%s: Atheros AR%s MAC/BB Rev:%x "
@@ -228,21 +239,109 @@ 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;
+ u32 wake_up_events;
+
+ wake_up_events = ath9k_wow_event_map(sc->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;
+
+ ath9k_hw_wow_enable(ah, wake_up_events);
+ 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;
+ if (!!sc->wow_triggers_enabled && device_can_wakeup(&pdev->dev))
+ ath9k_pci_wow_enable(sc);
+
ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
pci_save_state(pdev);
pci_disable_device(pdev);
+
+ if (!!sc->wow_triggers_enabled && device_can_wakeup(&pdev->dev)) {
+ pci_prepare_to_sleep(pdev);
+ return 0;
+ }
+
+ pci_wake_from_d3(pdev, !!sc->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 (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,
+ "Beacon miss interrupt picked up during sleep, "
+ "adding as possible wake up reason\n");
+ }
+
+ if (wow_status) {
+ DPRINTF(ah->ah_sc, ATH_DBG_ANY,
+ "Waking up due to WoW trigger%s\n",
+ ath9k_hw_wow_event_to_string(wow_status));
+ 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);
@@ -264,6 +363,9 @@ static int ath_pci_resume(struct pci_dev *pdev)
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+ if (sc->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..ca9073c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -0,0 +1,511 @@
+/*
+ * 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_CONFIG,
+ "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;
+ }
+}
+
+void 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;
+
+ /*
+ * 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);
+
+ 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_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);
+
+ /*
+ * 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);
+
+ /*
+ * 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;
+}
+
+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 reason";
+}
+
+#endif /* CONFIG_PM */
--
1.6.0.6
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support
2009-07-15 0:21 ` [PATCH 3/5] cfg80211: add WoW support Luis R. Rodriguez
@ 2009-07-15 11:45 ` Johannes Berg
2009-07-15 15:14 ` Luis R. Rodriguez
0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2009-07-15 11:45 UTC (permalink / raw)
To: Luis R. Rodriguez; +Cc: linville, linux-wireless, ath9k-devel
[-- Attachment #1: Type: text/plain, Size: 926 bytes --]
On Tue, 2009-07-14 at 20:21 -0400, Luis R. Rodriguez wrote:
> + /* Draw up a netlink message to send back */
> + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> + if (!msg) {
Need to use DEFAULT_SIZE or something.
> + if (!(wiphy->wow_triggers_supported & triggers_requested)) {
> + err = -EOPNOTSUPP;
> + goto out;
This seems wrong, should be "requested & supported != requested", I
think.
> @@ -4253,6 +4359,16 @@ static struct genl_ops nl80211_ops[] = {
> {
> .cmd = NL80211_CMD_DISCONNECT,
> .doit = nl80211_disconnect,
> + },
> + {
> + .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,
> },
This messes up the disconnect command.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support
2009-07-15 0:21 ` [PATCH 4/5] mac80211: " Luis R. Rodriguez
@ 2009-07-15 11:57 ` Johannes Berg
2009-07-15 15:22 ` Luis R. Rodriguez
0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2009-07-15 11:57 UTC (permalink / raw)
To: Luis R. Rodriguez; +Cc: linville, linux-wireless, ath9k-devel
[-- Attachment #1: Type: text/plain, Size: 634 bytes --]
On Tue, 2009-07-14 at 20:21 -0400, Luis R. Rodriguez wrote:
> If we're associated and have not already gone into PS mode
> we ensure send a nullfunc frame before going to suspend. Upon
> resume we ensure we restore PS mode if it was enabled and inform
> the driver accordingly, also notify the AP we're awake.
>
> WoW requires the radio to be left on during suspend, to do this
> we inform drivers of the requested wow triggers on the mac80211
> stop callback.
I don't think this patch would work if ath9k unprogrammed the hardware
correctly in ->remove_interface(), since you don't seem to skip that
call.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support
2009-07-15 11:45 ` Johannes Berg
@ 2009-07-15 15:14 ` Luis R. Rodriguez
2009-07-15 16:05 ` Johannes Berg
0 siblings, 1 reply; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 15:14 UTC (permalink / raw)
To: Johannes Berg
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
On Wed, Jul 15, 2009 at 04:45:22AM -0700, Johannes Berg wrote:
> On Tue, 2009-07-14 at 20:21 -0400, Luis R. Rodriguez wrote:
>
> > + /* Draw up a netlink message to send back */
> > + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
> > + if (!msg) {
>
> Need to use DEFAULT_SIZE or something.
Will change it, thanks. I see NLMSG_DEFAULT_SIZE is smaller than
NLMSG_GOODSIZE (it removes the NLMSG_HDRLEN), but no documentation
for either, when should I use either?
> > + if (!(wiphy->wow_triggers_supported & triggers_requested)) {
> > + err = -EOPNOTSUPP;
> > + goto out;
>
> This seems wrong, should be "requested & supported != requested", I
> think.
The above one won't return -EOPNOTSUPP when you add additional junk, you're
right, better to just inform userspace of that.
> > @@ -4253,6 +4359,16 @@ static struct genl_ops nl80211_ops[] = {
> > {
> > .cmd = NL80211_CMD_DISCONNECT,
> > .doit = nl80211_disconnect,
> > + },
> > + {
> > + .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,
> > },
>
> This messes up the disconnect command.
Must have been a rebase issue, strange, thanks, will fix up.
Luis
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support
2009-07-15 11:57 ` Johannes Berg
@ 2009-07-15 15:22 ` Luis R. Rodriguez
2009-07-15 16:03 ` Johannes Berg
0 siblings, 1 reply; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 15:22 UTC (permalink / raw)
To: Johannes Berg
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
On Wed, Jul 15, 2009 at 04:57:35AM -0700, Johannes Berg wrote:
> On Tue, 2009-07-14 at 20:21 -0400, Luis R. Rodriguez wrote:
> > If we're associated and have not already gone into PS mode
> > we ensure send a nullfunc frame before going to suspend. Upon
> > resume we ensure we restore PS mode if it was enabled and inform
> > the driver accordingly, also notify the AP we're awake.
> >
> > WoW requires the radio to be left on during suspend, to do this
> > we inform drivers of the requested wow triggers on the mac80211
> > stop callback.
>
> I don't think this patch would work if ath9k unprogrammed the hardware
> correctly in ->remove_interface(), since you don't seem to skip that
> call.
We just stop ANI in remove interface and reclaim beacon resources.
As per documentation it seems we are not correctly removing the MAC
address in ath9k for the removed interface to stop sending ACKs back.
I'd rather see that as a separate patch fix, but you do have a point.
As is right now though this doesn't affect WoW.
Luis
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support
2009-07-15 15:22 ` Luis R. Rodriguez
@ 2009-07-15 16:03 ` Johannes Berg
2009-07-15 16:18 ` Luis R. Rodriguez
0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2009-07-15 16:03 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
[-- Attachment #1: Type: text/plain, Size: 1324 bytes --]
On Wed, 2009-07-15 at 08:22 -0700, Luis R. Rodriguez wrote:
> On Wed, Jul 15, 2009 at 04:57:35AM -0700, Johannes Berg wrote:
> > On Tue, 2009-07-14 at 20:21 -0400, Luis R. Rodriguez wrote:
> > > If we're associated and have not already gone into PS mode
> > > we ensure send a nullfunc frame before going to suspend. Upon
> > > resume we ensure we restore PS mode if it was enabled and inform
> > > the driver accordingly, also notify the AP we're awake.
> > >
> > > WoW requires the radio to be left on during suspend, to do this
> > > we inform drivers of the requested wow triggers on the mac80211
> > > stop callback.
> >
> > I don't think this patch would work if ath9k unprogrammed the hardware
> > correctly in ->remove_interface(), since you don't seem to skip that
> > call.
>
> We just stop ANI in remove interface and reclaim beacon resources.
> As per documentation it seems we are not correctly removing the MAC
> address in ath9k for the removed interface to stop sending ACKs back.
> I'd rather see that as a separate patch fix, but you do have a point.
> As is right now though this doesn't affect WoW.
You haven't understood my point -- it makes your WoW patch invalid
because drivers that implement remove_interface properly cannot possibly
implement WoW this way.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support
2009-07-15 15:14 ` Luis R. Rodriguez
@ 2009-07-15 16:05 ` Johannes Berg
2009-07-15 16:16 ` Luis R. Rodriguez
0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2009-07-15 16:05 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
[-- Attachment #1: Type: text/plain, Size: 786 bytes --]
On Wed, 2009-07-15 at 08:14 -0700, Luis R. Rodriguez wrote:
> Will change it, thanks. I see NLMSG_DEFAULT_SIZE is smaller than
> NLMSG_GOODSIZE (it removes the NLMSG_HDRLEN), but no documentation
> for either, when should I use either?
I don't know, but GOODSIZE is larger than a page or something, iirc.
There was a patch recently changing all the users.
> > > + if (!(wiphy->wow_triggers_supported & triggers_requested)) {
> > > + err = -EOPNOTSUPP;
> > > + goto out;
> >
> > This seems wrong, should be "requested & supported != requested", I
> > think.
>
> The above one won't return -EOPNOTSUPP when you add additional junk, you're
> right, better to just inform userspace of that.
Your version also disallows turning off WoW again, afaict.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support
2009-07-15 16:05 ` Johannes Berg
@ 2009-07-15 16:16 ` Luis R. Rodriguez
2009-07-15 16:20 ` Johannes Berg
0 siblings, 1 reply; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 16:16 UTC (permalink / raw)
To: Johannes Berg
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
On Wed, Jul 15, 2009 at 09:05:06AM -0700, Johannes Berg wrote:
> On Wed, 2009-07-15 at 08:14 -0700, Luis R. Rodriguez wrote:
> > > > + if (!(wiphy->wow_triggers_supported & triggers_requested)) {
> > > > + err = -EOPNOTSUPP;
> > > > + goto out;
> > >
> > > This seems wrong, should be "requested & supported != requested", I
> > > think.
> >
> > The above one won't return -EOPNOTSUPP when you add additional junk, you're
> > right, better to just inform userspace of that.
>
> Your version also disallows turning off WoW again, afaict.
That is something we do want, as otherwise we'll keep the device powered on.
Will consider both.
Luis
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support
2009-07-15 16:03 ` Johannes Berg
@ 2009-07-15 16:18 ` Luis R. Rodriguez
0 siblings, 0 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 16:18 UTC (permalink / raw)
To: Johannes Berg
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
On Wed, Jul 15, 2009 at 09:03:51AM -0700, Johannes Berg wrote:
> On Wed, 2009-07-15 at 08:22 -0700, Luis R. Rodriguez wrote:
> > On Wed, Jul 15, 2009 at 04:57:35AM -0700, Johannes Berg wrote:
> > > On Tue, 2009-07-14 at 20:21 -0400, Luis R. Rodriguez wrote:
> > > > If we're associated and have not already gone into PS mode
> > > > we ensure send a nullfunc frame before going to suspend. Upon
> > > > resume we ensure we restore PS mode if it was enabled and inform
> > > > the driver accordingly, also notify the AP we're awake.
> > > >
> > > > WoW requires the radio to be left on during suspend, to do this
> > > > we inform drivers of the requested wow triggers on the mac80211
> > > > stop callback.
> > >
> > > I don't think this patch would work if ath9k unprogrammed the hardware
> > > correctly in ->remove_interface(), since you don't seem to skip that
> > > call.
> >
> > We just stop ANI in remove interface and reclaim beacon resources.
> > As per documentation it seems we are not correctly removing the MAC
> > address in ath9k for the removed interface to stop sending ACKs back.
> > I'd rather see that as a separate patch fix, but you do have a point.
> > As is right now though this doesn't affect WoW.
>
> You haven't understood my point -- it makes your WoW patch invalid
> because drivers that implement remove_interface properly cannot possibly
> implement WoW this way.
Ah sure, will respin and add some stuff for this...
Luis
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support
2009-07-15 16:16 ` Luis R. Rodriguez
@ 2009-07-15 16:20 ` Johannes Berg
2009-07-15 16:33 ` Luis R. Rodriguez
0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2009-07-15 16:20 UTC (permalink / raw)
To: Luis R. Rodriguez
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
[-- Attachment #1: Type: text/plain, Size: 876 bytes --]
On Wed, 2009-07-15 at 09:16 -0700, Luis R. Rodriguez wrote:
> On Wed, Jul 15, 2009 at 09:05:06AM -0700, Johannes Berg wrote:
> > On Wed, 2009-07-15 at 08:14 -0700, Luis R. Rodriguez wrote:
> > > > > + if (!(wiphy->wow_triggers_supported & triggers_requested)) {
> > > > > + err = -EOPNOTSUPP;
> > > > > + goto out;
> > > >
> > > > This seems wrong, should be "requested & supported != requested", I
> > > > think.
> > >
> > > The above one won't return -EOPNOTSUPP when you add additional junk, you're
> > > right, better to just inform userspace of that.
> >
> > Your version also disallows turning off WoW again, afaict.
>
> That is something we do want, as otherwise we'll keep the device powered on.
> Will consider both.
Huh? There has to be a way to turn off wow again after you've actually
resumed for other reasons, or whatever.
johannes
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 801 bytes --]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support
2009-07-15 16:20 ` Johannes Berg
@ 2009-07-15 16:33 ` Luis R. Rodriguez
0 siblings, 0 replies; 16+ messages in thread
From: Luis R. Rodriguez @ 2009-07-15 16:33 UTC (permalink / raw)
To: Johannes Berg
Cc: Luis Rodriguez, linville@tuxdriver.com,
linux-wireless@vger.kernel.org, ath9k-devel@lists.ath9k.org
On Wed, Jul 15, 2009 at 09:20:29AM -0700, Johannes Berg wrote:
> On Wed, 2009-07-15 at 09:16 -0700, Luis R. Rodriguez wrote:
> > On Wed, Jul 15, 2009 at 09:05:06AM -0700, Johannes Berg wrote:
> > > On Wed, 2009-07-15 at 08:14 -0700, Luis R. Rodriguez wrote:
> > > > > > + if (!(wiphy->wow_triggers_supported & triggers_requested)) {
> > > > > > + err = -EOPNOTSUPP;
> > > > > > + goto out;
> > > > >
> > > > > This seems wrong, should be "requested & supported != requested", I
> > > > > think.
> > > >
> > > > The above one won't return -EOPNOTSUPP when you add additional junk, you're
> > > > right, better to just inform userspace of that.
> > >
> > > Your version also disallows turning off WoW again, afaict.
> >
> > That is something we do want, as otherwise we'll keep the device powered on.
> > Will consider both.
>
> Huh? There has to be a way to turn off wow again after you've actually
> resumed for other reasons, or whatever.
Yes, we want that.
Luis
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2009-07-15 16:33 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-15 0:21 [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 1/5] ath9k: do not stop the queues in driver stop Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 2/5] adm8211: remove uneeded code during suspend/resume Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 3/5] cfg80211: add WoW support Luis R. Rodriguez
2009-07-15 11:45 ` Johannes Berg
2009-07-15 15:14 ` Luis R. Rodriguez
2009-07-15 16:05 ` Johannes Berg
2009-07-15 16:16 ` Luis R. Rodriguez
2009-07-15 16:20 ` Johannes Berg
2009-07-15 16:33 ` Luis R. Rodriguez
2009-07-15 0:21 ` [PATCH 4/5] mac80211: " Luis R. Rodriguez
2009-07-15 11:57 ` Johannes Berg
2009-07-15 15:22 ` Luis R. Rodriguez
2009-07-15 16:03 ` Johannes Berg
2009-07-15 16:18 ` Luis R. Rodriguez
2009-07-15 0:22 ` [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support Luis R. Rodriguez
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).