* [ath9k-devel] [PATCH 0/5] wireless: add wow support @ 2009-07-15 0:21 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* [PATCH 0/5] wireless: add wow support @ 2009-07-15 0:21 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 1/5] ath9k: do not stop the queues in driver stop 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 0:21 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* [PATCH 1/5] ath9k: do not stop the queues in driver stop @ 2009-07-15 0:21 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 2/5] adm8211: remove uneeded code during suspend/resume 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 0:21 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* [PATCH 2/5] adm8211: remove uneeded code during suspend/resume @ 2009-07-15 0:21 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 0:21 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 0:21 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 11:45 ` Johannes Berg -1 siblings, 0 replies; 37+ messages in thread From: Johannes Berg @ 2009-07-15 11:45 UTC (permalink / raw) To: ath9k-devel 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 -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 801 bytes Desc: This is a digitally signed message part Url : http://lists.ath9k.org/pipermail/ath9k-devel/attachments/20090715/ded12ee6/attachment.pgp ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 11:45 ` Johannes Berg 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 11:45 ` Johannes Berg @ 2009-07-15 15:14 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 15:14 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 15:14 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 15:14 ` Luis R. Rodriguez @ 2009-07-15 16:05 ` Johannes Berg -1 siblings, 0 replies; 37+ messages in thread From: Johannes Berg @ 2009-07-15 16:05 UTC (permalink / raw) To: ath9k-devel 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 -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 801 bytes Desc: This is a digitally signed message part Url : http://lists.ath9k.org/pipermail/ath9k-devel/attachments/20090715/d69ce2af/attachment-0001.pgp ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 16:05 ` Johannes Berg 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 16:05 ` Johannes Berg @ 2009-07-15 16:16 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 16:16 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 16:16 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 16:16 ` Luis R. Rodriguez @ 2009-07-15 16:20 ` Johannes Berg -1 siblings, 0 replies; 37+ messages in thread From: Johannes Berg @ 2009-07-15 16:20 UTC (permalink / raw) To: ath9k-devel 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 -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 801 bytes Desc: This is a digitally signed message part Url : http://lists.ath9k.org/pipermail/ath9k-devel/attachments/20090715/287aad5e/attachment.pgp ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 16:20 ` Johannes Berg 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support 2009-07-15 16:20 ` Johannes Berg @ 2009-07-15 16:33 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 16:33 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* Re: [PATCH 3/5] cfg80211: add WoW support @ 2009-07-15 16:33 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 0:21 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 0:21 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* [PATCH 4/5] mac80211: add WoW support @ 2009-07-15 0:21 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 11:57 ` Johannes Berg -1 siblings, 0 replies; 37+ messages in thread From: Johannes Berg @ 2009-07-15 11:57 UTC (permalink / raw) To: ath9k-devel 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 -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 801 bytes Desc: This is a digitally signed message part Url : http://lists.ath9k.org/pipermail/ath9k-devel/attachments/20090715/da7448d6/attachment.pgp ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support @ 2009-07-15 11:57 ` Johannes Berg 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2009-07-15 11:57 ` Johannes Berg @ 2009-07-15 15:22 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 15:22 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support @ 2009-07-15 15:22 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2009-07-15 15:22 ` Luis R. Rodriguez @ 2009-07-15 16:03 ` Johannes Berg -1 siblings, 0 replies; 37+ messages in thread From: Johannes Berg @ 2009-07-15 16:03 UTC (permalink / raw) To: ath9k-devel 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 -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 801 bytes Desc: This is a digitally signed message part Url : http://lists.ath9k.org/pipermail/ath9k-devel/attachments/20090715/4405ad17/attachment.pgp ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support @ 2009-07-15 16:03 ` Johannes Berg 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2009-07-15 16:03 ` Johannes Berg @ 2009-07-15 16:18 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 16:18 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* Re: [PATCH 4/5] mac80211: add WoW support @ 2009-07-15 16:18 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
* [ath9k-devel] [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support 2009-07-15 0:21 ` Luis R. Rodriguez @ 2009-07-15 0:22 ` Luis R. Rodriguez -1 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2009-07-15 0:22 UTC (permalink / raw) To: ath9k-devel 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] 37+ messages in thread
* [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support @ 2009-07-15 0:22 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ 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] 37+ messages in thread
[parent not found: <804B13F8F3D94A4AB18B9B01ACB68FA1037B5158@EXCHSRV.fokus.fraunhofer.de>]
* Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support [not found] <804B13F8F3D94A4AB18B9B01ACB68FA1037B5158@EXCHSRV.fokus.fraunhofer.de> @ 2010-07-27 17:20 ` Luis R. Rodriguez [not found] ` <804B13F8F3D94A4AB18B9B01ACB68FA1037B526B@EXCHSRV.fokus.fraunhofer.de> 2010-09-13 17:11 ` AW: " Simsek, Burak 0 siblings, 2 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2010-07-27 17:20 UTC (permalink / raw) To: Simsek, Burak; +Cc: Luis Rodriguez, linux-wireless On Tue, Jul 27, 2010 at 08:12:10AM -0700, Simsek, Burak wrote: > Dear Luis, > > I have found out that you have worked on WoW for ath9k for a while. However, > in the wiki page of wireless.kernel.org the current state is written as > ongoing work. Can you please tell me whether you were successful while > implementing WoW. Is there anything that we could use? WoW worked for me but inconsistantly and at the time of writing the patches I had to do quite a lot of coordination with Johannes since he had a lot of API changes and his changes needed to get merged first. I also found quite a few issues in mac80211 back then but I believe we have resolved all of them by now so I would expect that if the same WoW-only patches to be rebased and tested we may get better results. I stopped working on the WoW stuff due to lack of time to keep testing them but the last series can be edited to remove all of the already merged stuff and test out the new stuff. The more challenging thing for me was to actually get a WoW enabled 802.11 card, these are not as popular as you would hope. The EEPROM would be modified when WoW is enabled but WoW requires some actual hardware mucking to allow for the PCI Wake signal which is typically blocked off. If the EEPROM has WoW enabled then the hardware mucking would have been done. To test WoW you also need to drop Network Manager and use the supplicant directly so that during suspend you remain associated. I should note that WoW will only work on non-WPA networks with ath9k due to the lack of hardware CPU, during suspend there is only power for the 802.11 hardware, the CPU on your box would be asleep but would be required for group key changes. The way I was thinking of doing this was to only enable WoW through cfg80211 for ath9k if and only if you are associated and you are connected to a non WPA network. WoW would work with encryption on our USB devices where there is a CPU though. You can find my last series here: http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07-21.patch http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/iw-add-wow.patch If you manage to do what I noted above, get an actual WoW enabled card, and it works reliably I'd gladly welcome and ACK the patches :) Luis ^ permalink raw reply [flat|nested] 37+ messages in thread
[parent not found: <804B13F8F3D94A4AB18B9B01ACB68FA1037B526B@EXCHSRV.fokus.fraunhofer.de>]
* Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support [not found] ` <804B13F8F3D94A4AB18B9B01ACB68FA1037B526B@EXCHSRV.fokus.fraunhofer.de> @ 2010-07-29 15:31 ` Luis R. Rodriguez 2010-07-29 20:56 ` Luis R. Rodriguez 0 siblings, 1 reply; 37+ messages in thread From: Luis R. Rodriguez @ 2010-07-29 15:31 UTC (permalink / raw) To: Simsek, Burak, David Quan, Amod Bodas, Stephen Chen, Christian Lamparter Cc: Luis Rodriguez, linux-wireless On Thu, Jul 29, 2010 at 06:31:02AM -0700, Simsek, Burak wrote: > Hi Luis, > > we tried your patches on the old version as described in your patch file. > > As you wrote the first issue was about the getting the right hardware. We > first wanted to use RS71-USB from ubiquity and it has AR9280. However, we > then realized that the implementation was for pci. We had assumed that RS71 > would use ath9k but it used AR9170 driver. First question: do you think that > we can do similar patch for usb without much effort? Well, ar9170 uses Atheros radios but uses an old Zydas MAC so the programming would be different and I am not sure if ar9170 ever supported WoW. Stephen and Christian would know best at this point, whom I have CC'd. > Then we tried it with DWA-547 from DLink (phy0: Atheros AR9280 MAC/BB Rev:2 > AR5133 RF Rev:d0: mem=0xf8720000, irq=16), which also has AR9280. This is > the output we got: > [ 9.411217] ath9k: WoW capabler device: yes Neat. > [ 9.411226] ath9k: EEPROM indicates WoW hw changes in place: no Without this you won't get anywhere IIRC. This indicates that the card was actually modified to enable the PCIE WAKE signal, by default all Atheros devices have this wedged off. > [ 9.411234] ath9k: WoW exact match pattern support: no > [ 9.411241] ath9k: WoW match pattern first dword requirement: no > > Now, regarding those EEPROM problems we will have difficulties. Were you > able to get it running with a specific device? Can you recommend us > something? Well I can see if we can get docs out to let developers enable WoW on their own. This may take me some time to get and release. I should note enabling WoW requires some hw mods and then an EEPROM change. We cannot support allowing users to mod their EEPROMs at this point but if you do mod the hardware it would be simple enough to just modify the patch to not check for the EEPROM to enable WoW, as a starter. Your other best bet is to purchase a laptop that has WoW enabled but I unfortunately do not have a list of laptops/devices that have this enabled. > Our next step will be carrying the patch to the last version. Is there any > kind of documentation which could help us dealing with EEPROM issues? The EEPROM will be informative for the patch, that's all, now you can at least tell which cards have been moded or not, so now you just have to either get a WoW card or mode your card for it. If you mod it yourself you can then just disable the EEPROM check for testsing purposes (ie, patch won't be accepted upstream without the EEPROM check, but you can keep whatever you want on your end). Luis > > Best regards > > Burak Simsek > > > > > > -----Original Message----- > From: Luis R. Rodriguez [mailto:lrodriguez@atheros.com] > Sent: Dienstag, 27. Juli 2010 19:20 > To: Simsek, Burak > Cc: Luis Rodriguez; linux-wireless@vger.kernel.org > Subject: Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support > > On Tue, Jul 27, 2010 at 08:12:10AM -0700, Simsek, Burak wrote: > > Dear Luis, > > > > I have found out that you have worked on WoW for ath9k for a while. > However, > > in the wiki page of wireless.kernel.org the current state is written as > > ongoing work. Can you please tell me whether you were successful while > > implementing WoW. Is there anything that we could use? > > WoW worked for me but inconsistantly and at the time of writing > the patches I had to do quite a lot of coordination with Johannes > since he had a lot of API changes and his changes needed to get > merged first. > > I also found quite a few issues in mac80211 back then but I believe > we have resolved all of them by now so I would expect that if the > same WoW-only patches to be rebased and tested we may get better > results. > > I stopped working on the WoW stuff due to lack of time to keep > testing them but the last series can be edited to remove all > of the already merged stuff and test out the new stuff. The more > challenging thing for me was to actually get a WoW enabled > 802.11 card, these are not as popular as you would hope. The > EEPROM would be modified when WoW is enabled but WoW requires > some actual hardware mucking to allow for the PCI Wake signal > which is typically blocked off. If the EEPROM has WoW enabled > then the hardware mucking would have been done. > > To test WoW you also need to drop Network Manager and use > the supplicant directly so that during suspend you remain > associated. I should note that WoW will only work on > non-WPA networks with ath9k due to the lack of > hardware CPU, during suspend there is only power for the > 802.11 hardware, the CPU on your box would be asleep but > would be required for group key changes. The way I was thinking > of doing this was to only enable WoW through cfg80211 for ath9k if > and only if you are associated and you are connected to a non > WPA network. > > WoW would work with encryption on our USB devices where > there is a CPU though. > > You can find my last series here: > > http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07-21.pa > tch > http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/iw-add-wow.p > atch > > If you manage to do what I noted above, get an actual WoW enabled card, > and it works reliably I'd gladly welcome and ACK the patches :) > > Luis ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2010-07-29 15:31 ` Luis R. Rodriguez @ 2010-07-29 20:56 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2010-07-29 20:56 UTC (permalink / raw) To: Luis Rodriguez Cc: Simsek, Burak, David Quan, Amod Bodas, Stephen Chen, Christian Lamparter, linux-wireless On Thu, Jul 29, 2010 at 08:31:17AM -0700, Luis Rodriguez wrote: > On Thu, Jul 29, 2010 at 06:31:02AM -0700, Simsek, Burak wrote: > > Hi Luis, > > > > we tried your patches on the old version as described in your patch file. > > > > As you wrote the first issue was about the getting the right hardware. We > > first wanted to use RS71-USB from ubiquity and it has AR9280. However, we > > then realized that the implementation was for pci. We had assumed that RS71 > > would use ath9k but it used AR9170 driver. First question: do you think that > > we can do similar patch for usb without much effort? > > Well, ar9170 uses Atheros radios but uses an old Zydas MAC so the programming > would be different and I am not sure if ar9170 ever supported WoW. Stephen > and Christian would know best at this point, whom I have CC'd. I checked and ar9170 does not support WoW in hardware but since the firmware is open (GPLv2) a WoW solution might be possible through software. You'd just have to implement it. May want to look at carl9170. Luis ^ permalink raw reply [flat|nested] 37+ messages in thread
* AW: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2010-07-27 17:20 ` [ath9k-devel] [PATCH 4/5] mac80211: add WoW support Luis R. Rodriguez [not found] ` <804B13F8F3D94A4AB18B9B01ACB68FA1037B526B@EXCHSRV.fokus.fraunhofer.de> @ 2010-09-13 17:11 ` Simsek, Burak 2010-09-13 17:29 ` Luis R. Rodriguez 1 sibling, 1 reply; 37+ messages in thread From: Simsek, Burak @ 2010-09-13 17:11 UTC (permalink / raw) To: Luis R. Rodriguez; +Cc: Luis Rodriguez, linux-wireless [-- Attachment #1: Type: text/plain, Size: 5537 bytes --] Dear Luis, we are moving your patch to the latest version whenever we have the time to do so... You had sent us two patch files: http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07-21.pa tch http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/iw-add-wow.p atch The first one is successfully accomplished, but we have problems with the second one. I am giving a summary of the errors that we get below. There are files and variables defined in that patch that we cannot find anywhere and do not know what they should do. Can you please provide us with some hints regarding those errors? best regards Burak NL_CB_CUSTOM undeclared (first use in this function) wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 159 C/C++ Problem NL_CB_VALID undeclared (first use in this function) wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 159 C/C++ Problem NL_OK undeclared (first use in this function) wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 137 C/C++ Problem NL_SKIP undeclared (first use in this function) wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 129 C/C++ Problem stderr undeclared (first use in this function) wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 79 C/C++ Problem errno.h: No such file or directory wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 21 C/C++ Problem expected ) before ( token wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 162 C/C++ Problem expected ) before string constant wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 95 C/C++ Problem implicit declaration of function fprintf wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 79 C/C++ Problem implicit declaration of function genlmsg_attrdata wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 123 C/C++ Problem implicit declaration of function genlmsg_attrlen wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 124 C/C++ Problem implicit declaration of function nl_cb_set wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 159 C/C++ Problem implicit declaration of function nla_get_u32 wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 132 C/C++ Problem implicit declaration of function nla_parse wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 123 C/C++ Problem implicit declaration of function NLA_PUT_U32 wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 89 C/C++ Problem implicit declaration of function nlmsg_data wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 119 C/C++ Problem implicit declaration of function printf wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 128 C/C++ Problem iw.h: No such file or directory wow.c /wireless-testing/drivers/net/wireless/ath/ath9k line 32 C/C++ Problem -----Ursprüngliche Nachricht----- Von: Luis R. Rodriguez [mailto:lrodriguez@atheros.com] Gesendet: Dienstag, 27. Juli 2010 19:20 An: Simsek, Burak Cc: Luis Rodriguez; linux-wireless@vger.kernel.org Betreff: Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support On Tue, Jul 27, 2010 at 08:12:10AM -0700, Simsek, Burak wrote: > Dear Luis, > > I have found out that you have worked on WoW for ath9k for a while. > However, in the wiki page of wireless.kernel.org the current state is > written as ongoing work. Can you please tell me whether you were > successful while implementing WoW. Is there anything that we could use? WoW worked for me but inconsistantly and at the time of writing the patches I had to do quite a lot of coordination with Johannes since he had a lot of API changes and his changes needed to get merged first. I also found quite a few issues in mac80211 back then but I believe we have resolved all of them by now so I would expect that if the same WoW-only patches to be rebased and tested we may get better results. I stopped working on the WoW stuff due to lack of time to keep testing them but the last series can be edited to remove all of the already merged stuff and test out the new stuff. The more challenging thing for me was to actually get a WoW enabled 802.11 card, these are not as popular as you would hope. The EEPROM would be modified when WoW is enabled but WoW requires some actual hardware mucking to allow for the PCI Wake signal which is typically blocked off. If the EEPROM has WoW enabled then the hardware mucking would have been done. To test WoW you also need to drop Network Manager and use the supplicant directly so that during suspend you remain associated. I should note that WoW will only work on non-WPA networks with ath9k due to the lack of hardware CPU, during suspend there is only power for the 802.11 hardware, the CPU on your box would be asleep but would be required for group key changes. The way I was thinking of doing this was to only enable WoW through cfg80211 for ath9k if and only if you are associated and you are connected to a non WPA network. WoW would work with encryption on our USB devices where there is a CPU though. You can find my last series here: http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07-21.pa tch http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/iw-add-wow.p atch If you manage to do what I noted above, get an actual WoW enabled card, and it works reliably I'd gladly welcome and ACK the patches :) Luis [-- Attachment #2: smime.p7s --] [-- Type: application/pkcs7-signature, Size: 7083 bytes --] ^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2010-09-13 17:11 ` AW: " Simsek, Burak @ 2010-09-13 17:29 ` Luis R. Rodriguez 2010-09-14 9:53 ` AW: " Simsek, Burak 0 siblings, 1 reply; 37+ messages in thread From: Luis R. Rodriguez @ 2010-09-13 17:29 UTC (permalink / raw) To: Simsek, Burak; +Cc: Luis Rodriguez, linux-wireless On Mon, Sep 13, 2010 at 10:11 AM, Simsek, Burak <burak.simsek@fokus.fraunhofer.de> wrote: > Dear Luis, > > we are moving your patch to the latest version whenever we have the time to > do so... You had sent us two patch files: > > http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07-21.pa > tch > http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/iw-add-wow.p > atch > > > The first one is successfully accomplished, but we have problems with the > second one. I am giving a summary of the errors that we get below. There are > files and variables defined in that patch that we cannot find anywhere and > do not know what they should do. Can you please provide us with some hints > regarding those errors? The first one is the biggest chunk of work, I can take care of the second one. Can you post the first patch so I can test? BTW, I learned that we can test WoW in the community by using Apple boxen, Apple devices seems to always have WoW capable 802.11 Atheros chipsets. What are you using for your testing? Luis ^ permalink raw reply [flat|nested] 37+ messages in thread
* AW: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2010-09-13 17:29 ` Luis R. Rodriguez @ 2010-09-14 9:53 ` Simsek, Burak 2010-09-14 14:33 ` Luis R. Rodriguez 0 siblings, 1 reply; 37+ messages in thread From: Simsek, Burak @ 2010-09-14 9:53 UTC (permalink / raw) To: Luis R. Rodriguez; +Cc: Luis Rodriguez, linux-wireless [-- Attachment #1.1: Type: text/plain, Size: 2199 bytes --] Hi Luis, so far we could not find any device that worked. We ordered new ones... Will let you know in case we get something working. Attached you can find two patch files. Since we could not get any device working yet, I am not sure whether the patch is prepared correctly. But all your previous changes (from the first patch file) should be in. And we did not get compilation errors. Apple is interesting. We might give it a try as well. I ordered 2 mini-pci cards from sparklan (http://www.sparklan.com/product.php?func=view&prod_id=63) and (http://www.oxfordtec.com/us/p127/SparkLAN-WMIA-199N-WLAN-802.11n-draft-wifi-%202.4/5Ghz-dual-band-3T/3R-Module-%28Atheros-AR9001-AR9160-XSPAN%29-Wireless-miniP%20CI-card/product_info.html ) that I hope support wow. Best regards Burak Simsek -----Ursprüngliche Nachricht----- Von: mcgrof@gmail.com [mailto:mcgrof@gmail.com] Im Auftrag von Luis R. Rodriguez Gesendet: Montag, 13. September 2010 19:29 An: Simsek, Burak Cc: Luis Rodriguez; linux-wireless@vger.kernel.org Betreff: Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support On Mon, Sep 13, 2010 at 10:11 AM, Simsek, Burak <burak.simsek@fokus.fraunhofer.de> wrote: > Dear Luis, > > we are moving your patch to the latest version whenever we have the > time to do so... You had sent us two patch files: > > http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07 > -21.pa > tch > http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/iw-add > -wow.p > atch > > > The first one is successfully accomplished, but we have problems with > the second one. I am giving a summary of the errors that we get below. > There are files and variables defined in that patch that we cannot > find anywhere and do not know what they should do. Can you please > provide us with some hints regarding those errors? The first one is the biggest chunk of work, I can take care of the second one. Can you post the first patch so I can test? BTW, I learned that we can test WoW in the community by using Apple boxen, Apple devices seems to always have WoW capable 802.11 Atheros chipsets. What are you using for your testing? Luis [-- Attachment #1.2: 0001-mac80211-add-WoW-support.patch --] [-- Type: application/octet-stream, Size: 9965 bytes --] >From 91e3f7f301103d7f65d1d997fe10849d4093cdcb Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez <lrodriguez@atheros.com> Date: Tue, 12 May 2009 17:31:32 -0700 Subject: [PATCH 1/2] mac80211: add WoW support 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. When using WoW we do not remove the interface for the device we are using as a STA in mac80211. Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Signed-off-by: burak <burak@burak-desktop.(none)> --- 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/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/p54/main.c | 2 +- drivers/net/wireless/rtl818x/rtl8180_dev.c | 2 +- drivers/net/wireless/wl12xx/wl1251_main.c | 2 +- drivers/net/wireless/zd1211rw/zd_mac.c | 2 +- net/mac80211/debugfs.c | 4 ++-- net/mac80211/driver-trace.h | 6 ++++-- net/mac80211/main.c | 2 +- 17 files changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index f9aa1bc..fcaa286 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1543,7 +1543,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 91c5f73..cbd93b1 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1826,7 +1826,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 32bf79e..ce7cdb5 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1136,7 +1136,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/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1165f90..a57ead1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1291,7 +1291,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 a118652..7557533 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4512,7 +4512,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 67f18ec..e1422ae 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -3459,7 +3459,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 ad0e67f..f962528 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3475,7 +3475,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 68e624a..f9549ef 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -3220,7 +3220,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 5550755..3c4bf68 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -332,7 +332,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 92b486d..071229d 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -582,7 +582,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/p54/main.c b/drivers/net/wireless/p54/main.c index 622d27b..360685f 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -199,7 +199,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/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 05c6bad..540abda 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -650,7 +650,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/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index faf221c..1bde702 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -452,7 +452,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 43307bd..237c9ee 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -286,7 +286,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/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e81ef4e..ec9c403 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -112,8 +112,8 @@ 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_resume(&local->hw); + __ieee80211_suspend(&local->hw, NULL); + __ieee80211_resume(&local->hw, NULL); rtnl_unlock(); return count; diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index f6f3d89..34c935a 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -95,16 +95,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/main.c b/net/mac80211/main.c index 4935b84..3333491 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -295,7 +295,7 @@ static void ieee80211_restart_work(struct work_struct *work) container_of(work, struct ieee80211_local, restart_work); rtnl_lock(); - ieee80211_reconfig(local); + ieee80211_reconfig(local, NULL); rtnl_unlock(); } -- 1.7.0.4 [-- Attachment #1.3: 0002-Add-the-WoW-patch-of-Luis.patch --] [-- Type: application/octet-stream, Size: 56612 bytes --] >From 0d2ecb4947f18143e152874853c0dcf36f79d597 Mon Sep 17 00:00:00 2001 From: Burak Simsek <burak.simsekfokus.fraunhofer.de> Date: Tue, 14 Sep 2010 10:35:22 +0200 Subject: [PATCH 2/2] Add the WoW patch of Luis http://www.mk.kernel.org/pub/linux/kernel/people/mcgrof/patches/wow-07-21.patch --- drivers/net/wireless/ath/ath5k/base.c | 4 +- drivers/net/wireless/ath/ath9k/Makefile | 1 + drivers/net/wireless/ath/ath9k/ar9002_hw.c | 13 ++ drivers/net/wireless/ath/ath9k/ar9002_initvals.h | 32 ++++ drivers/net/wireless/ath/ath9k/ath9k.h | 20 +++ drivers/net/wireless/ath/ath9k/eeprom.h | 1 + drivers/net/wireless/ath/ath9k/eeprom_def.c | 2 + drivers/net/wireless/ath/ath9k/hw.c | 48 +++++++ drivers/net/wireless/ath/ath9k/hw.h | 62 ++++++++ drivers/net/wireless/ath/ath9k/main.c | 26 ++++- drivers/net/wireless/ath/ath9k/pci.c | 148 +++++++++++++++++++ drivers/net/wireless/ath/ath9k/reg.h | 153 ++++++++++++++++++++- drivers/net/wireless/mwl8k.c | 2 +- drivers/net/wireless/rt2x00/rt2x00.h | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- drivers/net/wireless/rtl818x/rtl8187_dev.c | 2 +- include/linux/nl80211.h | 37 +++++ include/net/cfg80211.h | 43 +++++- include/net/mac80211.h | 16 ++- net/mac80211/cfg.c | 8 +- net/mac80211/driver-ops.h | 7 +- net/mac80211/ieee80211_i.h | 23 ++- net/mac80211/iface.c | 6 +- net/mac80211/mlme.c | 3 +- net/mac80211/pm.c | 67 ++++++++- net/mac80211/util.c | 31 ++++- net/wireless/core.h | 3 + net/wireless/nl80211.c | 165 ++++++++++++++++++++++ net/wireless/sme.c | 14 ++ net/wireless/sysfs.c | 4 +- 30 files changed, 896 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 116ac66..5b3e5d3 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -224,7 +224,7 @@ static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath5k_txq *txq); static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan); 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_vif *vif); static void ath5k_remove_interface(struct ieee80211_hw *hw, @@ -3024,7 +3024,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/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 4555e99..0103ef5 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -10,6 +10,7 @@ ath9k-y += beacon.o \ ath9k-$(CONFIG_PCI) += pci.o ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o +ath9k-$(CONFIG_PM) += wow.o obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 303c63d..3e808d7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -103,6 +103,12 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) 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_9280_20_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9280Modes_9280_2, ARRAY_SIZE(ar9280Modes_9280_2), 6); @@ -118,6 +124,13 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) 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); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h index 6203eed..930de6d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9002_initvals.h @@ -919,6 +919,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 + static const u32 ar9285PciePhy_clkreq_always_on_L1_9285[][2] = { /* Addr allmodes */ {0x00004040, 0x9248fd00}, @@ -1760,6 +1776,22 @@ static const u32 ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { {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 */ + static const u32 ar9287Modes_9287_1_1[][6] = { {0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160, 0x000001e0}, {0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c, 0x000001e0}, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f0197a6..d61ea8f 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -589,6 +589,13 @@ struct ath_softc { int beacon_interval; +#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_DEBUGFS struct ath9k_debug debug; #endif @@ -599,6 +606,19 @@ struct ath_softc { struct ath_descdma txsdma; }; +#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/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 0b09db0..6a7317f 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -239,6 +239,7 @@ enum eeprom_param { EEP_MAC_MSW, EEP_MAC_MID, EEP_MAC_LSW, + EEP_WOW, EEP_REG_0, EEP_REG_1, EEP_OP_CAP, diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index afa2b73..921eee1 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -249,6 +249,8 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, return pBase->macAddr[2] << 8 | pBase->macAddr[3]; case EEP_MAC_MSW: return pBase->macAddr[4] << 8 | pBase->macAddr[5]; + case EEP_WOW: + return pBase->eepMisc & BIT(1); case EEP_REG_0: return pBase->regDmn[0]; case EEP_REG_1: diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 3384ca1..10bcdb2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -22,6 +22,8 @@ #include "hw-ops.h" #include "rc.h" #include "ar9003_mac.h" +#include "ar9002_initvals.h" + static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); @@ -545,6 +547,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath_print(common, ATH_DBG_RESET, "serialize_regmode is %d\n", ah->config.serialize_regmode); + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1; else @@ -605,6 +608,16 @@ static int __ath9k_hw_init(struct ath_hw *ah) return r; } + #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) || AR_SREV_9271(ah)) ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S); else @@ -617,6 +630,18 @@ static int __ath9k_hw_init(struct ath_hw *ah) return 0; } +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 ); +} + + int ath9k_hw_init(struct ath_hw *ah) { int ret; @@ -2078,6 +2103,29 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) "regdomain mapped to 0x%x\n", regulatory->current_rd); } + eeval = ah->eep_ops->get_eeprom(ah, EEP_WOW); + if (AR_SREV_9280_10_OR_LATER(ah)) { + /* + * Devices >= AR9280 rev 10 are capable of WoW but require + * a few hardware changes. The EEPROM tells us whether these + * hardware changes are in place on the device. Just enabling + * WoW on the EEPROM is not enough to enable WoW. + */ + pCap->wow |= ATH9K_WOW_DEVICE_CAPABLE; + if (eeval) { + pCap->wow |= ATH9K_WOW_HW_ENABLED; + pCap->wow |= ATH9K_WOW_PATTERN_MATCH_EXACT; + /* + * First 4 bytes of all patterns must match, + * requires some wide masks to ensure proper + * functionality for the disassociation and + * deauthentication frames. + */ + if (AR_SREV_9280(ah)) + pCap->wow |= ATH9K_WOW_PATTERN_MATCH_DWORD; + } + } + eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); if ((eeval & (AR5416_OPFLAGS_11G | AR5416_OPFLAGS_11A)) == 0) { ath_print(common, ATH_DBG_FATAL, diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1601dd4..a89351c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -206,6 +206,42 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_PAPRD = BIT(22), }; +/** + * enum ath9k_hw_wow_caps - WoW device capabilities + * + * @ATH9K_WOW_DEVICE_CAPABLE: device revision is capable of WoW + * @ATH9K_WOW_HW_ENABLED: EEPROM indicates hardware changes for WoW + * *should be* are present. Devices need some the PCI Wake signal to go + * through, this signal is typically capped with a resistor. + * Devices with WoW physically enabled have this resistor + * shorted to 0 ohm and also have the register that PCI core + * picks up as PCI PME capable enabled. + * ATH9K_WOW_PATTERN_MATCH_EXACT: device is capable of matching + * an exact pattern for de-authentication and disassocation. + * ATH9K_WOW_PATTERN_MATCH_DWORD: device requires the first four + * bytes of the pattern for deauthentication and disassocation + * to match for all types of possible frames received of those + * types. + */ +enum ath9k_hw_wow_caps { + ATH9K_WOW_DEVICE_CAPABLE = BIT(0), + ATH9K_WOW_HW_ENABLED = BIT(1), + ATH9K_WOW_PATTERN_MATCH_EXACT = BIT(2), + ATH9K_WOW_PATTERN_MATCH_DWORD = BIT(3), +}; + + +/** + * struct ath9k_hw_capabilities - device capabilities + * + * This structure contains the device capabilities. The EEPROM will + * be read and specific device capabilities are mapped accordingly + * for easy access. + * + * wow: Wake-on-Wireless LAN capabilities, can be any of the + * %ATH9K_WOW_*. + */ + struct ath9k_hw_capabilities { u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ DECLARE_BITMAP(wireless_modes, ATH9K_MODE_MAX); /* ATH9K_MODE_* */ @@ -221,6 +257,7 @@ struct ath9k_hw_capabilities { u8 num_gpio_pins; u8 num_antcfg_2ghz; u8 num_antcfg_5ghz; + u32 wow; u8 rx_hp_qdepth; u8 rx_lp_qdepth; u8 rx_status_len; @@ -785,6 +822,13 @@ struct ath_hw { int PDADCdelta; u8 led_pin; + +#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; @@ -815,6 +859,11 @@ struct ath_hw { struct ar5416IniArray iniRadio[ATH_INI_NUM_SPLIT]; struct ar5416IniArray iniSOC[ATH_INI_NUM_SPLIT]; +#ifdef CONFIG_PM + /* SerDes values during WOW sleep */ + struct ar5416IniArray iniPcieSerdesWow; +#endif + u32 intr_gen_timer_trigger; u32 intr_gen_timer_thresh; struct ath_gen_timer_table hw_gen_timers; @@ -1005,4 +1054,17 @@ void ath9k_hw_attach_ani_ops_new(struct ath_hw *ah); #define ATH9K_CLOCK_RATE_2GHZ_OFDM 44 #define ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM 44 +#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/main.c b/drivers/net/wireless/ath/ath9k/main.c index a57ead1..4344a87 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -645,7 +645,7 @@ irqreturn_t ath_isr(int irq, void *dev) struct ath_hw *ah = sc->sc_ah; enum ath9k_int status; bool sched = false; - + struct ath_common *common = ath9k_hw_common(ah); /* * The hardware is not ready/present, don't * touch anything. Note this can happen early @@ -725,6 +725,21 @@ irqreturn_t ath_isr(int irq, void *dev) ath9k_hw_set_interrupts(ah, ah->imask); } + if (status & ATH9K_INT_BMISS) { + #ifdef CONFIG_PM + if (sc->wow_sleep_proc_intr) { + ath_print(common, (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 */ + ath_print(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 @@ -1320,6 +1335,15 @@ static void ath9k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow) cancel_work_sync(&sc->chan_work); } +#ifdef CONFIG_PM + if (wow && wow->triggers_enabled) { + sc->wow_triggers_enabled = wow->triggers_enabled; + ath_print(common, ATH_DBG_ANY, "Leaving radio on during " + "suspend for WoW\n"); + return; + } +#endif + if (sc->sc_flags & SC_OP_INVALID) { ath_print(common, ATH_DBG_ANY, "Device not present\n"); mutex_unlock(&sc->mutex); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index b5b6514..8311a0a 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -156,6 +156,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"); @@ -212,6 +215,34 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) wiphy_info(hw->wiphy, "%s mem=0x%lx, irq=%d\n", hw_name, (unsigned long)mem, pdev->irq); + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + /* WoW is for PCI for ath9k */ + + /* + * Bmiss and link change triggers not yet fully tested, add them + * after properly testing and confirming behaviour. I believe + * we need pattern matching support to enable them as well + * (detect deauth and disassoc). + */ + if (ah->caps.wow & ATH9K_WOW_DEVICE_CAPABLE && + ah->caps.wow & ATH9K_WOW_HW_ENABLED) + hw->wiphy->wow_triggers_supported = + NL80211_WOW_TRIGGER_MAGIC_PACKET; + + ath_print(common, ATH_DBG_CONFIG, + "WoW capabler device: %s\n", + (ah->caps.wow & ATH9K_WOW_DEVICE_CAPABLE) ? "yes" : "no"); + ath_print(common, ATH_DBG_CONFIG, + "EEPROM indicates WoW hw changes in place: %s\n", + (ah->caps.wow & ATH9K_WOW_HW_ENABLED) ? "yes" : "no"); + ath_print(common, ATH_DBG_CONFIG, + "WoW exact match pattern support: %s\n", + (ah->caps.wow & ATH9K_WOW_PATTERN_MATCH_EXACT) ? "yes" : "no"); + ath_print(common, ATH_DBG_CONFIG, + "WoW match pattern first dword requirement: %s\n", + (ah->caps.wow & ATH9K_WOW_PATTERN_MATCH_DWORD) ? "yes" : "no"); + return 0; err_init: @@ -247,21 +278,134 @@ 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 0 + 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; +#endif + + return wow_events; +} + +static void ath9k_pci_wow_enable(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + u32 wake_up_events; + struct ath_common *common = ath9k_hw_common(ah); + 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. + */ + //burak> resmen uctum burada + sc->wow_intr_before_sleep = ah->imask; + ath9k_hw_set_interrupts(ah, ATH9K_INT_BMISS | ATH9K_INT_GLOBAL); + ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL; + + ath9k_hw_wow_enable(ah, wake_up_events); + device_set_wakeup_enable(sc->dev, 1); + + ath_print(common, ATH_DBG_ANY, + "WoW enabled\n"); + + sc->wow_sleep_proc_intr = true; + + /* + 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, sc->sc_ah->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); + ah->imask = sc->wow_intr_before_sleep; + struct ath_common *common = ath9k_hw_common(ah); + + 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; + ath_print(common, ATH_DBG_ANY, + "Beacon miss interrupt picked up during sleep, " + "adding as possible wake up reason\n"); + } + + if (wow_status) { + ath_print(common, ATH_DBG_ANY, + "Waking up due to WoW trigger%s\n", + ath9k_hw_wow_event_to_string(wow_status)); + ath_print(common, 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); @@ -285,6 +429,10 @@ 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, sc->sc_ah->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 d01c4ad..2891d65 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -707,8 +707,9 @@ #define AR_WA_D3_TO_L1_DISABLE_REAL (1 << 16) #define AR_WA_ASPM_TIMER_BASED_DISABLE (1 << 17) #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 AR_WA_UNTIE_RESET_EN (1 << 15) /* Enable PCI Reset to POR (power-on-reset) */ +#define AR_WA_ANALOG_SHIFT (1 << 20) +#define AR_WA_POR_SHORT (1 << 21) /* PCI-E Phy reset control */ #define AR9285_WA_DEFAULT 0x004a050b #define AR9280_WA_DEFAULT 0x0040073b #define AR_WA_DEFAULT 0x0000073f @@ -1824,6 +1825,154 @@ enum { #define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20) #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)) #define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */ #define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */ diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f152a25..5a711c3 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3256,7 +3256,7 @@ static int mwl8k_start(struct ieee80211_hw *hw) return rc; } -static void mwl8k_stop(struct ieee80211_hw *hw) +static void mwl8k_stop(struct ieee80211_hw *hw, struct cfg80211_wow *wow) { struct mwl8k_priv *priv = hw->priv; int i; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 0ae942c..0e59b84 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1082,7 +1082,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_vif *vif); 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 235e037..92a95cd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -175,7 +175,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/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 38fa824..f62f464 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -987,7 +987,7 @@ rtl8187_start_exit: return ret; } -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/include/linux/nl80211.h b/include/linux/nl80211.h index 31603e8..805048c 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -311,6 +311,19 @@ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices * associated with this wiphy must be down and will follow. * + * @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_REMAIN_ON_CHANNEL: Request to remain awake on the specified * channel for the specified amount of time. This can be used to do * off-channel operations like transmit a Public Action frame and wait for @@ -462,6 +475,8 @@ enum nl80211_commands { NL80211_CMD_DISCONNECT, NL80211_CMD_SET_WIPHY_NETNS, + NL80211_CMD_GET_WOW, + NL80211_CMD_SET_WOW, NL80211_CMD_GET_SURVEY, NL80211_CMD_NEW_SURVEY_RESULTS, @@ -509,6 +524,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 @@ -736,6 +753,12 @@ enum nl80211_commands { * * @NL80211_ATTR_PID: Process ID of a network namespace. * + * @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_GENERATION: Used to indicate consistent snapshots for * dumps. This number increases whenever the object list being * dumped changes, and as such userspace can verify that it has @@ -927,6 +950,9 @@ enum nl80211_attrs { NL80211_ATTR_KEYS, NL80211_ATTR_PID, + + NL80211_ATTR_WOW_TRIGGERS_SUPPORTED, + NL80211_ATTR_WOW_TRIGGERS_ENABLED, NL80211_ATTR_4ADDR, @@ -1703,6 +1729,17 @@ enum nl80211_key_attributes { }; /** + * 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. + */ +enum nl80211_wow_triggers { + NL80211_WOW_TRIGGER_MAGIC_PACKET = 1 << 1, +}; + + +/** * enum nl80211_tx_rate_attributes - TX rate set attributes * @__NL80211_TXRATE_INVALID: invalid * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 4c8c727..2d1ba87 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -780,6 +780,9 @@ struct cfg80211_crypto_settings { bool control_port_no_encrypt; }; + + + /** * struct cfg80211_auth_request - Authentication request data * @@ -977,6 +980,27 @@ struct cfg80211_pmksa { u8 *pmkid; }; + +/* + * 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_*. + * @netdev: net_device where we enabled WoW. This is only used to clear + * the @triggers_enabled upon disassociation and to prevent settings + * onto other netdevices from the same rdev. + */ +struct cfg80211_wow { + u32 triggers_enabled; + struct net_device *netdev; +}; + + /** * struct cfg80211_ops - backend description for wireless configuration * @@ -990,8 +1014,11 @@ struct cfg80211_pmksa { * 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 - * @resume: wiphy device needs to be resumed + * @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. We pass the struct cfg80211_wow + * so the device can opt out from adding new information for the AP we + * we were last associated to when WoW was enabled * * @add_virtual_intf: create a new virtual interface with the given name, * must set the struct wireless_dev's iftype. Beware: You must create @@ -1115,8 +1142,11 @@ struct cfg80211_pmksa { * */ struct cfg80211_ops { - int (*suspend)(struct wiphy *wiphy); - int (*resume)(struct wiphy *wiphy); +// int (*suspend)(struct wiphy *wiphy); +// int (*resume)(struct wiphy *wiphy); + + int (*suspend)(struct wiphy *wiphy, struct cfg80211_wow *wow); + int (*resume)(struct wiphy *wiphy, struct cfg80211_wow *wow); int (*add_virtual_intf)(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, @@ -1337,6 +1367,10 @@ struct ieee80211_txrx_stypes { * @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. * @_net: the network namespace this wiphy currently lives in * @perm_addr: permanent MAC address of this device * @addr_mask: If the device supports multiple MAC addresses by masking, @@ -1414,6 +1448,7 @@ struct wiphy { u32 hw_version; u8 max_num_pmkids; + 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 diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f91fc33..c04b222 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1520,12 +1520,14 @@ enum ieee80211_ampdu_mlme_action { * Must be implemented and can sleep. * * @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. If you added any work onto the mac80211 workqueue - * you should ensure to cancel it on this callback. - * Must be implemented and can sleep. + * 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 @@ -1696,7 +1698,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_vif *vif); int (*change_interface)(struct ieee80211_hw *hw, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 5de1ca3..ba85860 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1132,14 +1132,14 @@ 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) +static int ieee80211_resume(struct wiphy *wiphy, struct cfg80211_wow *wow) { - return __ieee80211_resume(wiphy_priv(wiphy)); + return __ieee80211_resume(wiphy_priv(wiphy) ,wow); } #else #define ieee80211_suspend NULL diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 6064b7b..82a695d 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -24,12 +24,13 @@ 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) { might_sleep(); - trace_drv_stop(local); - local->ops->stop(&local->hw); + trace_drv_stop(local, wow); + local->ops->stop(&local->hw, wow); + trace_drv_return_void(local); /* sync away all work on the tasklet before clearing started */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 16f7fb1..5313ecd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -723,6 +723,13 @@ struct ieee80211_local { * ease timer cancelling etc. */ 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; /* device is started */ bool started; @@ -1178,23 +1185,25 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, size_t len); /* Suspend/resume and hw reconfiguration */ -int ieee80211_reconfig(struct ieee80211_local *local); -void ieee80211_stop_device(struct ieee80211_local *local); +int ieee80211_reconfig(struct ieee80211_local *local, struct cfg80211_wow *wow); +void ieee80211_stop_device(struct ieee80211_local *local, struct cfg80211_wow *wow); #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) +static inline int __ieee80211_resume(struct ieee80211_hw *hw, + struct cfg80211_wow *wow) { - return ieee80211_reconfig(hw_to_local(hw)); + return ieee80211_reconfig(hw_to_local(hw), wow); } #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; } -static inline int __ieee80211_resume(struct ieee80211_hw *hw) +static inline int __ieee80211_resume(struct ieee80211_hw *hw, + struct cfg80211_wow *wow) { return 0; } diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index c1cc200..78506a6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -216,7 +216,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) if (!is_valid_ether_addr(dev->dev_addr)) { if (!local->open_count) - drv_stop(local); + drv_stop(local,NULL); return -EADDRNOTAVAIL; } } @@ -338,7 +338,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) drv_remove_interface(local, &sdata->vif); 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) @@ -543,7 +543,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (local->ops->napi_poll) napi_disable(&local->napi); ieee80211_clear_tx_pending(local); - ieee80211_stop_device(local); + ieee80211_stop_device(local,NULL); /* no reconfiguring after stop! */ hw_reconf_flags = 0; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0cb822c..9072293 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1912,7 +1912,8 @@ static void ieee80211_sta_monitor_work(struct work_struct *work) struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.monitor_work); - + if (sdata->local->scanning) + return; ieee80211_mgd_probe_ap(sdata, false); } diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index d287fde..37bd195 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -6,14 +6,68 @@ #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; struct sta_info *sta; + + bool wow_netdev_found = false; 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); + + /* + * Send nullfunc PS frame to AP so it can buffer frames for us, + * unless we already are in that PS state. We'll discard all + * frames except the ones our harware will process and use for + * triggering a wake up. + */ + list_for_each_entry(sdata, &local->interfaces, list) { + struct ieee80211_if_managed *ifmgd; + if (sdata->dev != wow->netdev) + continue; + /* WoW only applies to STAs */ + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) + continue; + ifmgd = &sdata->u.mgd; + /* + * cfg80211 should have cleared the + * triggers upon disassociation. + */ + if (WARN_ON(!ifmgd->associated)) + continue; + if (!local->ps_before_suspend) + ieee80211_send_nullfunc(local, sdata, 1); + wow_netdev_found = true; + break; + } + + /* + * WoW triggers should be cleared if the netdev claiming + * it no longer exists + */ + WARN_ON(!wow_netdev_found); + + } + + if (wow_netdev_found) + printk(KERN_DEBUG "mac80211: WoW enabled interface found, " + "will try to enable WoW during suspend\n"); + + + ieee80211_stop_queues_by_reason(hw, IEEE80211_QUEUE_STOP_REASON_SUSPEND); @@ -69,6 +123,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) switch(sdata->vif.type) { case NL80211_IFTYPE_STATION: ieee80211_sta_quiesce(sdata); + /* Don't remove the interface when WoW is enabled */ + if (sdata->dev != wow->netdev) + continue; break; case NL80211_IFTYPE_ADHOC: ieee80211_ibss_quiesce(sdata); @@ -94,9 +151,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) drv_remove_interface(local, &sdata->vif); } - /* stop hardware - this must stop RX */ - if (local->open_count) - ieee80211_stop_device(local); + + if (wow_netdev_found) + ieee80211_stop_device(local, wow); + else if (local->open_count) + ieee80211_stop_device(local, NULL); local->suspended = true; /* need suspended to be visible before quiescing is false */ diff --git a/net/mac80211/util.c b/net/mac80211/util.c index bd40b11..bda9f53 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1083,26 +1083,30 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local, return supp_rates; } -void ieee80211_stop_device(struct ieee80211_local *local) +void ieee80211_stop_device(struct ieee80211_local *local, struct cfg80211_wow *wow) { ieee80211_led_radio(local, false); cancel_work_sync(&local->reconfig_filter); flush_workqueue(local->workqueue); - drv_stop(local); + drv_stop(local, wow); } -int ieee80211_reconfig(struct ieee80211_local *local) +int ieee80211_reconfig(struct ieee80211_local *local, struct cfg80211_wow *wow) { struct ieee80211_hw *hw = &local->hw; struct ieee80211_sub_if_data *sdata; struct sta_info *sta; int res; + bool from_suspend = local->suspended; if (local->suspended) local->resuming = true; + if (from_suspend && local->ps_before_suspend) + local->hw.conf.flags |= IEEE80211_CONF_PS; + /* restart hardware */ if (local->open_count) { /* @@ -1122,11 +1126,16 @@ int ieee80211_reconfig(struct ieee80211_local *local) ieee80211_led_radio(local, true); } - /* add interfaces */ + /* + * Add all interfaces back again. If we had enabled WoW we + * don't need to add the interface as we never removed it + * at suspend. + */ list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && - ieee80211_sdata_running(sdata)) + ieee80211_sdata_running(sdata) && + sdata->dev != wow->netdev) res = drv_add_interface(local, &sdata->vif); } @@ -1235,6 +1244,18 @@ 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); + } + } + /* first set suspended false, then resuming */ local->suspended = false; mb(); diff --git a/net/wireless/core.h b/net/wireless/core.h index 58ab2c7..80c69b0 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -69,6 +69,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; /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 85a23de..75c73b0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -142,6 +142,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, [NL80211_ATTR_PID] = { .type = NLA_U32 }, + [NL80211_ATTR_WOW_TRIGGERS_SUPPORTED] = { .type = NLA_U32 }, + [NL80211_ATTR_WOW_TRIGGERS_ENABLED] = { .type = NLA_U32 }, [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, .len = WLAN_PMKID_LEN }, @@ -4470,6 +4472,153 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) 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, &rdev, &dev); + if (err) + goto out_rtnl; + + wiphy = &rdev->wiphy; + + /* Draw up a netlink message to send back */ + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, 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_reply(msg, info); + 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; + struct wireless_dev *wdev; + u32 triggers_requested; + + if (!info->attrs[NL80211_ATTR_WOW_TRIGGERS_ENABLED]) + return -EINVAL; + + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info, &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]); + + if (rdev->wow.netdev) { + if (rdev->wow.netdev != dev) { + err = -EALREADY; + goto out; + } + if (!triggers_requested) { + rdev->wow.triggers_enabled = 0; + rdev->wow.netdev = NULL; + goto out; + } + } + + /* This would be a no-op as the rdev->wow should be unconfigured */ + if (!triggers_requested) + goto out; + + /* We only support magic packet right now */ + if (triggers_requested & ~NL80211_WOW_TRIGGER_MAGIC_PACKET) { + err = -EOPNOTSUPP; + goto out; + } + + if ((triggers_requested & wiphy->wow_triggers_supported) != + triggers_requested) { + err = -EOPNOTSUPP; + goto out; + } + + /* + * It only makes sense to enable WoW if we're associated as a STA, + * the AP should be buffering frames for us. We'll discard all frames + * and only process the frames which will trigger us on in hardware. + */ + wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + + if (wdev->iftype != NL80211_IFTYPE_STATION) { + err = -EINVAL; + wdev_unlock(wdev); + goto out; + } + + if (wdev->sme_state != CFG80211_SME_CONNECTED) { + err = -ENOTCONN; + wdev_unlock(wdev); + goto out; + } + + wdev_unlock(wdev); + + /* + * Apply changes. This information gets passed to the + * drivers during suspend. + */ + rdev->wow.netdev = dev; + rdev->wow.triggers_enabled = triggers_requested; + + out: + /* cleanup */ + cfg80211_unlock_rdev(rdev); + dev_put(dev); + out_rtnl: + rtnl_unlock(); + + return err; + } + static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; @@ -5154,6 +5303,22 @@ 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, + }, + + { .cmd = NL80211_CMD_GET_INTERFACE, .doit = nl80211_get_interface, diff --git a/net/wireless/sme.c b/net/wireless/sme.c index a8c2d6b..ec066a6 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -704,7 +704,21 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->wext.connect.ssid_len = 0; #endif +/* + * If we're not associated we cannot possibly get frames + * to generate a WoW trigger. We also do this for security + * purposes as with 802.11 we can roam to untrusted networks. + * We require userspace to send a request to enable WoW for + * each BSS. + */ + if (rdev->wow.netdev && rdev->wow.netdev == dev) { + rdev->wow.netdev = NULL; + rdev->wow.triggers_enabled = 0; + } + schedule_work(&cfg80211_disconnect_work); + + } void cfg80211_disconnected(struct net_device *dev, u16 reason, diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 74a9e3c..31807e2 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -84,7 +84,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(); } @@ -103,7 +103,7 @@ static int wiphy_resume(struct device *dev) if (rdev->ops->resume) { rtnl_lock(); - ret = rdev->ops->resume(&rdev->wiphy); + ret = rdev->ops->resume(&rdev->wiphy, &rdev->wow); rtnl_unlock(); } -- 1.7.0.4 [-- Attachment #2: smime.p7s --] [-- Type: application/pkcs7-signature, Size: 7083 bytes --] ^ permalink raw reply related [flat|nested] 37+ messages in thread
* Re: [ath9k-devel] [PATCH 4/5] mac80211: add WoW support 2010-09-14 9:53 ` AW: " Simsek, Burak @ 2010-09-14 14:33 ` Luis R. Rodriguez 0 siblings, 0 replies; 37+ messages in thread From: Luis R. Rodriguez @ 2010-09-14 14:33 UTC (permalink / raw) To: Simsek, Burak; +Cc: Luis Rodriguez, linux-wireless On Tue, Sep 14, 2010 at 2:53 AM, Simsek, Burak <burak.simsek@fokus.fraunhofer.de> wrote: > Hi Luis, > > so far we could not find any device that worked. We ordered new ones... Will let you know in case we get something working. > Attached you can find two patch files. Since we could not get any device working yet, I am not sure whether the patch is prepared correctly. But all your previous changes (from the first patch file) should be in. And we did not get compilation errors. > > Apple is interesting. We might give it a try as well. I ordered 2 mini-pci cards from sparklan (http://www.sparklan.com/product.php?func=view&prod_id=63) and (http://www.oxfordtec.com/us/p127/SparkLAN-WMIA-199N-WLAN-802.11n-draft-wifi-%202.4/5Ghz-dual-band-3T/3R-Module-%28Atheros-AR9001-AR9160-XSPAN%29-Wireless-miniP%20CI-card/product_info.html ) that I hope support wow. Can you re-spllit all the WoW patches as I did into separate patches and post a link to a directoy where I can download each one? Luis ^ permalink raw reply [flat|nested] 37+ messages in thread
end of thread, other threads:[~2010-09-14 14:34 UTC | newest]
Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-15 0:21 [ath9k-devel] [PATCH 0/5] wireless: add wow support Luis R. Rodriguez
2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 0:21 ` [ath9k-devel] [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 ` [ath9k-devel] [PATCH 2/5] adm8211: remove uneeded code during suspend/resume Luis R. Rodriguez
2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 0:21 ` [ath9k-devel] [PATCH 3/5] cfg80211: add WoW support Luis R. Rodriguez
2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 11:45 ` [ath9k-devel] " Johannes Berg
2009-07-15 11:45 ` Johannes Berg
2009-07-15 15:14 ` [ath9k-devel] " Luis R. Rodriguez
2009-07-15 15:14 ` Luis R. Rodriguez
2009-07-15 16:05 ` [ath9k-devel] " Johannes Berg
2009-07-15 16:05 ` Johannes Berg
2009-07-15 16:16 ` [ath9k-devel] " Luis R. Rodriguez
2009-07-15 16:16 ` Luis R. Rodriguez
2009-07-15 16:20 ` [ath9k-devel] " Johannes Berg
2009-07-15 16:20 ` Johannes Berg
2009-07-15 16:33 ` [ath9k-devel] " Luis R. Rodriguez
2009-07-15 16:33 ` Luis R. Rodriguez
2009-07-15 0:21 ` [ath9k-devel] [PATCH 4/5] mac80211: " Luis R. Rodriguez
2009-07-15 0:21 ` Luis R. Rodriguez
2009-07-15 11:57 ` [ath9k-devel] " Johannes Berg
2009-07-15 11:57 ` Johannes Berg
2009-07-15 15:22 ` [ath9k-devel] " Luis R. Rodriguez
2009-07-15 15:22 ` Luis R. Rodriguez
2009-07-15 16:03 ` [ath9k-devel] " Johannes Berg
2009-07-15 16:03 ` Johannes Berg
2009-07-15 16:18 ` [ath9k-devel] " Luis R. Rodriguez
2009-07-15 16:18 ` Luis R. Rodriguez
2009-07-15 0:22 ` [ath9k-devel] [PATCH 5/5] ath9k: Add Wake-on-Wireless-LAN support Luis R. Rodriguez
2009-07-15 0:22 ` Luis R. Rodriguez
[not found] <804B13F8F3D94A4AB18B9B01ACB68FA1037B5158@EXCHSRV.fokus.fraunhofer.de>
2010-07-27 17:20 ` [ath9k-devel] [PATCH 4/5] mac80211: add WoW support Luis R. Rodriguez
[not found] ` <804B13F8F3D94A4AB18B9B01ACB68FA1037B526B@EXCHSRV.fokus.fraunhofer.de>
2010-07-29 15:31 ` Luis R. Rodriguez
2010-07-29 20:56 ` Luis R. Rodriguez
2010-09-13 17:11 ` AW: " Simsek, Burak
2010-09-13 17:29 ` Luis R. Rodriguez
2010-09-14 9:53 ` AW: " Simsek, Burak
2010-09-14 14:33 ` Luis R. Rodriguez
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.