summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-ops.h19
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c77
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h49
-rw-r--r--drivers/net/wireless/ath/ath10k/wow.c89
7 files changed, 245 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index ffcf30756b9e..f194d434b97c 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -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 {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 1147dee4051e..9fcc2866d830 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -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);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index 8bb01c1a35f7..dcb0da51530a 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -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,
@@ -1187,6 +1189,23 @@ ath10k_wmi_sta_keepalive(struct ath10k *ar,
}
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)
{
struct sk_buff *skb;
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 95bd588e0d2c..b904a0d9d391 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -3010,6 +3010,82 @@ ath10k_wmi_tlv_op_gen_tdls_peer_update(struct ath10k *ar,
}
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)
{
struct wmi_tlv_wow_enable_cmd *cmd;
@@ -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,
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index 7140e5e23307..1d4aed14fbb4 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -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;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index f59e5f86708b..9b17ef150303 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -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.
diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c
index de678fd3b801..1a43375c3264 100644
--- a/drivers/net/wireless/ath/ath10k/wow.c
+++ b/drivers/net/wireless/ath/ath10k/wow.c
@@ -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) {