Commit 0f84a156 authored by Baochen Qiang's avatar Baochen Qiang Committed by Kalle Valo

ath11k: Handle keepalive during WoWLAN suspend and resume

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: default avatarBaochen Qiang <quic_bqiang@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220506012540.1579604-1-quic_bqiang@quicinc.com
parent 9497b788
......@@ -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;
}
......@@ -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
......@@ -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);
}
......@@ -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
......@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment