]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ath11k: Handle keepalive during WoWLAN suspend and resume
authorBaochen Qiang <quic_bqiang@quicinc.com>
Mon, 9 May 2022 11:57:31 +0000 (14:57 +0300)
committerKalle Valo <quic_kvalo@quicinc.com>
Tue, 10 May 2022 16:30:37 +0000 (19:30 +0300)
With WoWLAN enabled and after sleeping for a rather long time,
we are seeing that with some APs, it is not able to wake up
the STA though the correct wake up pattern has been configured.
This is because the host doesn't send keepalive command to
firmware, thus firmware will not send any packet to the AP and
after a specific time the AP kicks out the STA.

Fix this issue by enabling keepalive before going to suspend
and disabling it after resume back.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: Baochen Qiang <quic_bqiang@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220506012540.1579604-1-quic_bqiang@quicinc.com
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/mac.h
drivers/net/wireless/ath/ath11k/wmi.c
drivers/net/wireless/ath/ath11k/wmi.h
drivers/net/wireless/ath/ath11k/wow.c

index 9b28fdac3c942e5d9998533ca0f2f2004155c740..7fdc39b7205e1651b94c4cdedbb4bd703a37942c 100644 (file)
@@ -9035,3 +9035,34 @@ void ath11k_mac_destroy(struct ath11k_base *ab)
                pdev->ar = NULL;
        }
 }
+
+int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
+                                enum wmi_sta_keepalive_method method,
+                                u32 interval)
+{
+       struct ath11k *ar = arvif->ar;
+       struct wmi_sta_keepalive_arg arg = {};
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+               return 0;
+
+       if (!test_bit(WMI_TLV_SERVICE_STA_KEEP_ALIVE, ar->ab->wmi_ab.svc_map))
+               return 0;
+
+       arg.vdev_id = arvif->vdev_id;
+       arg.enabled = 1;
+       arg.method = method;
+       arg.interval = interval;
+
+       ret = ath11k_wmi_sta_keepalive(ar, &arg);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to set keepalive on vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
index 7f93e3a9ca2330522edbbe4991d326f98d1c9a86..57ebfc592b001b3b4fbf14a0e87ec3230ae393a3 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <net/mac80211.h>
 #include <net/cfg80211.h>
+#include "wmi.h"
 
 struct ath11k;
 struct ath11k_base;
@@ -173,4 +174,7 @@ void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb);
 void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id);
 void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif);
 int ath11k_mac_wait_tx_complete(struct ath11k *ar);
+int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif,
+                                enum wmi_sta_keepalive_method method,
+                                u32 interval);
 #endif
index 1410114d1d5cb800f2a50ec7d7baf76529e3618d..63463b6c63be353c38c19f97ad8eb0a17bb4dc84 100644 (file)
@@ -8959,3 +8959,44 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
 
        return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
 }
+
+int ath11k_wmi_sta_keepalive(struct ath11k *ar,
+                            const struct wmi_sta_keepalive_arg *arg)
+{
+       struct ath11k_pdev_wmi *wmi = ar->wmi;
+       struct wmi_sta_keepalive_cmd *cmd;
+       struct wmi_sta_keepalive_arp_resp *arp;
+       struct sk_buff *skb;
+       size_t len;
+
+       len = sizeof(*cmd) + sizeof(*arp);
+       skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_sta_keepalive_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                    WMI_TAG_STA_KEEPALIVE_CMD) |
+                                    FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+       cmd->vdev_id = arg->vdev_id;
+       cmd->enabled = arg->enabled;
+       cmd->interval = arg->interval;
+       cmd->method = arg->method;
+
+       if (arg->method == WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE ||
+           arg->method == WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST) {
+               arp = (struct wmi_sta_keepalive_arp_resp *)(cmd + 1);
+               arp->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                            WMI_TAG_STA_KEEPALVE_ARP_RESPONSE) |
+                                FIELD_PREP(WMI_TLV_LEN, sizeof(*arp) - TLV_HDR_SIZE);
+               arp->src_ip4_addr = arg->src_ip4_addr;
+               arp->dest_ip4_addr = arg->dest_ip4_addr;
+               ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr);
+       }
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "wmi sta keepalive vdev %d enabled %d method %d interval %d\n",
+                  arg->vdev_id, arg->enabled, arg->method, arg->interval);
+
+       return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
+}
index 7600e9a52da823475876e0b46325489f3d7abe7b..b1fad4707dc60b49a060df05861b7f5d3e281815 100644 (file)
@@ -5907,6 +5907,50 @@ struct wmi_pdev_set_geo_table_cmd {
        u32 rsvd_len;
 } __packed;
 
+struct wmi_sta_keepalive_cmd {
+       u32 tlv_header;
+       u32 vdev_id;
+       u32 enabled;
+
+       /* WMI_STA_KEEPALIVE_METHOD_ */
+       u32 method;
+
+       /* in seconds */
+       u32 interval;
+
+       /* following this structure is the TLV for struct
+        * wmi_sta_keepalive_arp_resp
+        */
+} __packed;
+
+struct wmi_sta_keepalive_arp_resp {
+       u32 tlv_header;
+       u32 src_ip4_addr;
+       u32 dest_ip4_addr;
+       struct wmi_mac_addr dest_mac_addr;
+} __packed;
+
+struct wmi_sta_keepalive_arg {
+       u32 vdev_id;
+       u32 enabled;
+       u32 method;
+       u32 interval;
+       u32 src_ip4_addr;
+       u32 dest_ip4_addr;
+       const u8 dest_mac_addr[ETH_ALEN];
+};
+
+enum wmi_sta_keepalive_method {
+       WMI_STA_KEEPALIVE_METHOD_NULL_FRAME = 1,
+       WMI_STA_KEEPALIVE_METHOD_UNSOLICITED_ARP_RESPONSE = 2,
+       WMI_STA_KEEPALIVE_METHOD_ETHERNET_LOOPBACK = 3,
+       WMI_STA_KEEPALIVE_METHOD_GRATUITOUS_ARP_REQUEST = 4,
+       WMI_STA_KEEPALIVE_METHOD_MGMT_VENDOR_ACTION = 5,
+};
+
+#define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT     30
+#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE     0
+
 int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
                        u32 cmd_id);
 struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len);
@@ -6087,5 +6131,7 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
                                 struct ath11k_vif *arvif);
 int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val);
 int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar);
+int ath11k_wmi_sta_keepalive(struct ath11k *ar,
+                            const struct wmi_sta_keepalive_arg *arg);
 
 #endif
index 9d088cebef03c64f70a2995f7b62a7203f90c6b0..b3e65cd13d8349cab54413abcf3dc6cd44a36e7e 100644 (file)
@@ -640,6 +640,24 @@ static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable)
        return 0;
 }
 
+static int ath11k_wow_set_keepalive(struct ath11k *ar,
+                                   enum wmi_sta_keepalive_method method,
+                                   u32 interval)
+{
+       struct ath11k_vif *arvif;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               ret = ath11k_mac_vif_set_keepalive(arvif, method, interval);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
                          struct cfg80211_wowlan *wowlan)
 {
@@ -691,6 +709,14 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
                goto cleanup;
        }
 
+       ret = ath11k_wow_set_keepalive(ar,
+                                      WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
+                                      WMI_STA_KEEPALIVE_INTERVAL_DEFAULT);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to enable wow keepalive: %d\n", ret);
+               goto cleanup;
+       }
+
        ret = ath11k_wow_enable(ar->ab);
        if (ret) {
                ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
@@ -786,6 +812,14 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw)
                goto exit;
        }
 
+       ret = ath11k_wow_set_keepalive(ar,
+                                      WMI_STA_KEEPALIVE_METHOD_NULL_FRAME,
+                                      WMI_STA_KEEPALIVE_INTERVAL_DISABLE);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to disable wow keepalive: %d\n", ret);
+               goto exit;
+       }
+
 exit:
        if (ret) {
                switch (ar->state) {