ath6kl: Add WOW suspend/resume implementation
authorRaja Mani <rmani@qca.qualcomm.com>
Mon, 7 Nov 2011 20:52:45 +0000 (22:52 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 11 Nov 2011 10:59:59 +0000 (12:59 +0200)
This is the core WOW suspend/resume functions will be called
in PM suspend/resume path.

Signed-off-by: Raja Mani <rmani@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.h

index 44e2c76f92468bd736955ecb1c76b36a99c44287..2e12c6f70a5a2336984b4271ab2e99ff9d196c10 100644 (file)
@@ -1644,6 +1644,115 @@ static int ath6kl_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev)
        return 0;
 }
 
+static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow)
+{
+       struct ath6kl_vif *vif;
+       int ret, pos, left;
+       u32 filter = 0;
+       u16 i;
+       u8 mask[WOW_MASK_SIZE];
+
+       vif = ath6kl_vif_first(ar);
+       if (!vif)
+               return -EIO;
+
+       if (!ath6kl_cfg80211_ready(vif))
+               return -EIO;
+
+       if (!test_bit(CONNECTED, &vif->flags))
+               return -EINVAL;
+
+       /* Clear existing WOW patterns */
+       for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++)
+               ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx,
+                                              WOW_LIST_ID, i);
+       /* Configure new WOW patterns */
+       for (i = 0; i < wow->n_patterns; i++) {
+
+               /*
+                * Convert given nl80211 specific mask value to equivalent
+                * driver specific mask value and send it to the chip along
+                * with patterns. For example, If the mask value defined in
+                * struct cfg80211_wowlan is 0xA (equivalent binary is 1010),
+                * then equivalent driver specific mask value is
+                * "0xFF 0x00 0xFF 0x00".
+                */
+               memset(&mask, 0, sizeof(mask));
+               for (pos = 0; pos < wow->patterns[i].pattern_len; pos++) {
+                       if (wow->patterns[i].mask[pos / 8] & (0x1 << (pos % 8)))
+                               mask[pos] = 0xFF;
+               }
+               /*
+                * Note: Pattern's offset is not passed as part of wowlan
+                * parameter from CFG layer. So it's always passed as ZERO
+                * to the firmware. It means, given WOW patterns are always
+                * matched from the first byte of received pkt in the firmware.
+                */
+               ret = ath6kl_wmi_add_wow_pattern_cmd(ar->wmi,
+                                       vif->fw_vif_idx, WOW_LIST_ID,
+                                       wow->patterns[i].pattern_len,
+                                       0 /* pattern offset */,
+                                       wow->patterns[i].pattern, mask);
+               if (ret)
+                       return ret;
+       }
+
+       if (wow->disconnect)
+               filter |= WOW_FILTER_OPTION_NWK_DISASSOC;
+
+       if (wow->magic_pkt)
+               filter |= WOW_FILTER_OPTION_MAGIC_PACKET;
+
+       if (wow->gtk_rekey_failure)
+               filter |= WOW_FILTER_OPTION_GTK_ERROR;
+
+       if (wow->eap_identity_req)
+               filter |= WOW_FILTER_OPTION_EAP_REQ;
+
+       if (wow->four_way_handshake)
+               filter |= WOW_FILTER_OPTION_8021X_4WAYHS;
+
+       ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx,
+                                         ATH6KL_WOW_MODE_ENABLE,
+                                         filter,
+                                         WOW_HOST_REQ_DELAY);
+       if (ret)
+               return ret;
+
+       ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+                                                ATH6KL_HOST_MODE_ASLEEP);
+       if (ret)
+               return ret;
+
+       if (ar->tx_pending[ar->ctrl_ep]) {
+               left = wait_event_interruptible_timeout(ar->event_wq,
+                               ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT);
+               if (left == 0) {
+                       ath6kl_warn("clear wmi ctrl data timeout\n");
+                       ret = -ETIMEDOUT;
+               } else if (left < 0) {
+                       ath6kl_warn("clear wmi ctrl data failed: %d\n", left);
+                       ret = left;
+               }
+       }
+
+       return ret;
+}
+
+static int ath6kl_wow_resume(struct ath6kl *ar)
+{
+       struct ath6kl_vif *vif;
+       int ret;
+
+       vif = ath6kl_vif_first(ar);
+       if (!vif)
+               return -EIO;
+
+       ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx,
+                                                ATH6KL_HOST_MODE_AWAKE);
+       return ret;
+}
+
 int ath6kl_cfg80211_suspend(struct ath6kl *ar,
                            enum ath6kl_cfg_suspend_mode mode)
 {
index c30642e9795dfc7820a79914f47e5f8263217b8c..b6442c1af3f4a557404e2a53f6b48da0d56ef5b2 100644 (file)
@@ -439,6 +439,9 @@ struct ath6kl_vif {
        struct target_stats target_stats;
 };
 
+#define WOW_LIST_ID            0
+#define WOW_HOST_REQ_DELAY     500 /* ms */
+
 /* Flag info */
 enum ath6kl_dev_state {
        WMI_ENABLED,