ath10k: add support for wowlan offload for wcn3990 WLAN module
Define data structure and callback for the ARP and NS offload for the ath10k wcn3990 WLAN module. Configure the ARP offload parameter and enable ARP offload request flag for each vdev if the vdev in station mode. This feature adds support to wakeup the device from sleep state through wowlan event if the ARP request pattern matched with pattern configured by WLAN module during wow suspend. CRs-Fixed: 2146265 Change-Id: I54c3491493ad46a7db2adbf71ae1668cf9a88e51 Signed-off-by: Sarada Prasanna Garnayak <sgarna@codeaurora.org>
This commit is contained in:
parent
af5f70355c
commit
2fde186579
7 changed files with 245 additions and 2 deletions
|
@ -24,6 +24,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <soc/qcom/socinfo.h>
|
||||
|
||||
#include "htt.h"
|
||||
|
@ -430,6 +431,8 @@ struct ath10k_vif {
|
|||
struct work_struct ap_csa_work;
|
||||
struct delayed_work connection_loss_work;
|
||||
struct cfg80211_bitrate_mask bitrate_mask;
|
||||
struct wmi_ns_arp_offload_req arp_offload;
|
||||
struct wmi_ns_arp_offload_req ns_offload;
|
||||
};
|
||||
|
||||
struct ath10k_vif_iter {
|
||||
|
|
|
@ -1387,6 +1387,10 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
|
|||
|
||||
lockdep_assert_held(&ar->conf_mutex);
|
||||
|
||||
/* Clear arp and ns offload cache */
|
||||
memset(&arvif->arp_offload, 0, sizeof(arvif->arp_offload));
|
||||
memset(&arvif->ns_offload, 0, sizeof(arvif->ns_offload));
|
||||
|
||||
reinit_completion(&ar->vdev_setup_done);
|
||||
reinit_completion(&ar->vdev_delete_done);
|
||||
|
||||
|
|
|
@ -160,6 +160,8 @@ struct wmi_ops {
|
|||
u32 num_ac);
|
||||
struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar,
|
||||
const struct wmi_sta_keepalive_arg *arg);
|
||||
struct sk_buff *(*gen_set_arp_ns_offload)(struct ath10k *ar,
|
||||
struct ath10k_vif *arvif);
|
||||
struct sk_buff *(*gen_wow_enable)(struct ath10k *ar);
|
||||
struct sk_buff *(*gen_wow_add_wakeup_event)(struct ath10k *ar, u32 vdev_id,
|
||||
enum wmi_wow_wakeup_event event,
|
||||
|
@ -1186,6 +1188,23 @@ ath10k_wmi_sta_keepalive(struct ath10k *ar,
|
|||
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ath10k_wmi_set_arp_ns_offload(struct ath10k *ar, struct ath10k_vif *arvif)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u32 cmd_id;
|
||||
|
||||
if (!ar->wmi.ops->gen_set_arp_ns_offload)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
skb = ar->wmi.ops->gen_set_arp_ns_offload(ar, arvif);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
cmd_id = ar->wmi.cmd->set_arp_ns_offload_cmdid;
|
||||
return ath10k_wmi_cmd_send(ar, skb, cmd_id);
|
||||
}
|
||||
|
||||
static inline int
|
||||
ath10k_wmi_wow_enable(struct ath10k *ar)
|
||||
{
|
||||
|
|
|
@ -3009,6 +3009,82 @@ ath10k_wmi_tlv_op_gen_tdls_peer_update(struct ath10k *ar,
|
|||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar,
|
||||
struct ath10k_vif *arvif)
|
||||
{
|
||||
struct wmi_tlv_arp_ns_offload_cmd *cmd;
|
||||
struct wmi_tlv *tlv;
|
||||
struct sk_buff *skb;
|
||||
size_t len;
|
||||
void *ptr;
|
||||
int i;
|
||||
struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload;
|
||||
struct wmi_ns_offload *ns_tuple[WMI_MAX_NS_OFFLOADS];
|
||||
struct wmi_arp_offload *arp_tuple[WMI_MAX_ARP_OFFLOADS];
|
||||
|
||||
len = sizeof(*cmd) + sizeof(*tlv) +
|
||||
sizeof(*tlv) + WMI_MAX_NS_OFFLOADS *
|
||||
(sizeof(struct wmi_ns_offload) + sizeof(*tlv)) +
|
||||
sizeof(*tlv) + WMI_MAX_ARP_OFFLOADS *
|
||||
(sizeof(struct wmi_arp_offload) + sizeof(*tlv));
|
||||
|
||||
skb = ath10k_wmi_alloc_skb(ar, len);
|
||||
if (!skb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ptr = (void *)skb->data;
|
||||
tlv = ptr;
|
||||
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD);
|
||||
tlv->len = __cpu_to_le16(sizeof(*cmd));
|
||||
cmd = (struct wmi_tlv_arp_ns_offload_cmd *)tlv->value;
|
||||
cmd->flags = __cpu_to_le32(0);
|
||||
cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
|
||||
|
||||
ptr += (sizeof(*tlv) + sizeof(*cmd));
|
||||
tlv = ptr;
|
||||
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
|
||||
tlv->len = __cpu_to_le16(WMI_MAX_NS_OFFLOADS *
|
||||
(sizeof(struct wmi_ns_offload) + sizeof(*tlv)));
|
||||
ptr += sizeof(*tlv);
|
||||
tlv = ptr;
|
||||
|
||||
for (i = 0; i < WMI_MAX_NS_OFFLOADS; i++) {
|
||||
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE);
|
||||
tlv->len = __cpu_to_le16(sizeof(struct wmi_ns_offload));
|
||||
ns_tuple[i] = (struct wmi_ns_offload *)tlv->value;
|
||||
ns_tuple[i]->flags |= __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE);
|
||||
ptr += (sizeof(*tlv) + sizeof(struct wmi_ns_offload));
|
||||
tlv = ptr;
|
||||
}
|
||||
|
||||
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
|
||||
tlv->len = __cpu_to_le16(WMI_MAX_ARP_OFFLOADS *
|
||||
(sizeof(struct wmi_arp_offload) + sizeof(*tlv)));
|
||||
ptr += sizeof(*tlv);
|
||||
tlv = ptr;
|
||||
|
||||
for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) {
|
||||
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE);
|
||||
tlv->len = __cpu_to_le16(sizeof(struct wmi_arp_offload));
|
||||
arp_tuple[i] = (struct wmi_arp_offload *)tlv->value;
|
||||
if (arp->enable_offload && (i == 0)) {
|
||||
arp_tuple[i]->flags |=
|
||||
__cpu_to_le32(WMI_ARPOFF_FLAGS_VALID);
|
||||
memcpy(&arp_tuple[i]->target_ipaddr,
|
||||
&arp->params.ipv4_addr, 4);
|
||||
} else {
|
||||
arp_tuple[i]->flags |=
|
||||
__cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE);
|
||||
}
|
||||
ptr += (sizeof(*tlv) + sizeof(struct wmi_arp_offload));
|
||||
tlv = ptr;
|
||||
}
|
||||
|
||||
ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set arp ns offload\n");
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
ath10k_wmi_tlv_op_gen_wow_enable(struct ath10k *ar)
|
||||
{
|
||||
|
@ -3695,6 +3771,7 @@ static const struct wmi_ops wmi_tlv_ops = {
|
|||
.gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie,
|
||||
.gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd,
|
||||
.gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive,
|
||||
.gen_set_arp_ns_offload = ath10k_wmi_tlv_op_gen_set_arp_ns_offload,
|
||||
.gen_wow_enable = ath10k_wmi_tlv_op_gen_wow_enable,
|
||||
.gen_wow_add_wakeup_event = ath10k_wmi_tlv_op_gen_wow_add_wakeup_event,
|
||||
.gen_wow_host_wakeup_ind = ath10k_wmi_tlv_gen_wow_host_wakeup_ind,
|
||||
|
|
|
@ -1557,6 +1557,12 @@ struct wmi_tlv_wow_enable_cmd {
|
|||
__le32 pause_iface_config;
|
||||
} __packed;
|
||||
|
||||
struct wmi_tlv_arp_ns_offload_cmd {
|
||||
__le32 flags;
|
||||
__le32 vdev_id;
|
||||
__le32 num_ns_ext_tuples;
|
||||
} __packed;
|
||||
|
||||
struct wmi_tlv_wow_host_wakeup_ind {
|
||||
__le32 reserved;
|
||||
} __packed;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
#include <net/mac80211.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/in.h>
|
||||
|
||||
/*
|
||||
* This file specifies the WMI interface for the Unified Software
|
||||
|
@ -2884,6 +2886,53 @@ struct wmi_start_scan_common {
|
|||
__le32 scan_ctrl_flags;
|
||||
} __packed;
|
||||
|
||||
/* ARP-NS offload data structure */
|
||||
#define WMI_NSOFF_MAX_TARGET_IPS 2
|
||||
#define WMI_MAX_NS_OFFLOADS 2
|
||||
#define WMI_MAX_ARP_OFFLOADS 2
|
||||
#define WMI_ARPOFF_FLAGS_VALID BIT(0)
|
||||
#define WMI_IPV4_ARP_REPLY_OFFLOAD 0
|
||||
#define WMI_ARP_NS_OFFLOAD_DISABLE 0
|
||||
#define WMI_ARP_NS_OFFLOAD_ENABLE 1
|
||||
|
||||
struct wmi_ns_offload_info {
|
||||
struct in6_addr src_addr;
|
||||
struct in6_addr self_addr[TARGET_NUM_STATIONS];
|
||||
struct in6_addr target_addr[TARGET_NUM_STATIONS];
|
||||
struct wmi_mac_addr self_macaddr;
|
||||
u8 src_ipv6_addr_valid;
|
||||
struct in6_addr target_addr_valid;
|
||||
struct in6_addr target_addr_ac_type;
|
||||
u8 slot_idx;
|
||||
} __packed;
|
||||
|
||||
struct wmi_ns_arp_offload_req {
|
||||
u8 offload_type;
|
||||
u8 enable_offload;
|
||||
__le32 num_ns_offload_count;
|
||||
union {
|
||||
struct in_addr ipv4_addr;
|
||||
struct in6_addr ipv6_addr;
|
||||
} params;
|
||||
struct wmi_ns_offload_info offload_info;
|
||||
struct wmi_mac_addr bssid;
|
||||
} __packed;
|
||||
|
||||
struct wmi_ns_offload {
|
||||
__le32 flags;
|
||||
struct in6_addr target_ipaddr[WMI_NSOFF_MAX_TARGET_IPS];
|
||||
struct in6_addr solicitation_ipaddr;
|
||||
struct in6_addr remote_ipaddr;
|
||||
struct wmi_mac_addr target_mac;
|
||||
} __packed;
|
||||
|
||||
struct wmi_arp_offload {
|
||||
__le32 flags;
|
||||
struct in_addr target_ipaddr;
|
||||
struct in_addr remote_ipaddr;
|
||||
struct wmi_mac_addr target_mac;
|
||||
} __packed;
|
||||
|
||||
struct wmi_start_scan_tlvs {
|
||||
/* TLV parameters. These includes channel list, ssid list, bssid list,
|
||||
* extra ies.
|
||||
|
|
|
@ -224,6 +224,76 @@ static int ath10k_wow_wakeup(struct ath10k *ar)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif,
|
||||
bool enable_offload)
|
||||
{
|
||||
struct in_device *in_dev;
|
||||
struct in_ifaddr *ifa;
|
||||
bool offload_params_found = false;
|
||||
struct wireless_dev *wdev = ieee80211_vif_to_wdev(arvif->vif);
|
||||
struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload;
|
||||
|
||||
if (!enable_offload) {
|
||||
arp->offload_type = __cpu_to_le16(WMI_IPV4_ARP_REPLY_OFFLOAD);
|
||||
arp->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_DISABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!wdev)
|
||||
return -ENODEV;
|
||||
if (!wdev->netdev)
|
||||
return -ENODEV;
|
||||
in_dev = __in_dev_get_rtnl(wdev->netdev);
|
||||
if (!in_dev)
|
||||
return -ENODEV;
|
||||
|
||||
arp->offload_type = __cpu_to_le16(WMI_IPV4_ARP_REPLY_OFFLOAD);
|
||||
arp->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_ENABLE);
|
||||
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
|
||||
if (!strcmp(ifa->ifa_label, wdev->netdev->name)) {
|
||||
offload_params_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!offload_params_found)
|
||||
return -ENODEV;
|
||||
|
||||
memcpy(&arp->params.ipv4_addr, &ifa->ifa_local, 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath10k_wow_enable_ns_arp_offload(struct ath10k *ar, bool offload)
|
||||
{
|
||||
struct ath10k_vif *arvif;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(arvif, &ar->arvifs, list) {
|
||||
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
|
||||
continue;
|
||||
|
||||
if (!arvif->is_up)
|
||||
continue;
|
||||
|
||||
ret = ath10k_wow_fill_vdev_arp_offload_struct(arvif, offload);
|
||||
if (ret) {
|
||||
ath10k_err(ar, "ARP-offload config failed, vdev: %d\n",
|
||||
arvif->vdev_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ath10k_wmi_set_arp_ns_offload(ar, arvif);
|
||||
if (ret) {
|
||||
ath10k_err(ar, "failed to send offload cmd, vdev: %d\n",
|
||||
arvif->vdev_id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath10k_config_wow_listen_interval(struct ath10k *ar)
|
||||
{
|
||||
int ret;
|
||||
|
@ -263,11 +333,17 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
|
|||
goto exit;
|
||||
}
|
||||
|
||||
ret = ath10k_wow_enable_ns_arp_offload(ar, true);
|
||||
if (ret) {
|
||||
ath10k_warn(ar, "failed to enable ARP-NS offload: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = ath10k_wow_cleanup(ar);
|
||||
if (ret) {
|
||||
ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
|
||||
ret);
|
||||
goto exit;
|
||||
goto disable_ns_arp_offload;
|
||||
}
|
||||
|
||||
ret = ath10k_wow_set_wakeups(ar, wowlan);
|
||||
|
@ -304,6 +380,9 @@ wakeup:
|
|||
cleanup:
|
||||
ath10k_wow_cleanup(ar);
|
||||
|
||||
disable_ns_arp_offload:
|
||||
ath10k_wow_enable_ns_arp_offload(ar, false);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
return ret ? 1 : 0;
|
||||
|
@ -341,8 +420,14 @@ int ath10k_wow_op_resume(struct ieee80211_hw *hw)
|
|||
}
|
||||
|
||||
ret = ath10k_wow_wakeup(ar);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = ath10k_wow_enable_ns_arp_offload(ar, false);
|
||||
if (ret)
|
||||
ath10k_warn(ar, "failed to disable ARP-NS offload: %d\n", ret);
|
||||
|
||||
exit:
|
||||
if (ret) {
|
||||
|
|
Loading…
Add table
Reference in a new issue