summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c33
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h14
-rw-r--r--drivers/net/wireless/ath/ath10k/wow.c118
3 files changed, 150 insertions, 15 deletions
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 2dc2b5360ee8..d938ca951aee 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -3123,13 +3123,14 @@ ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar,
void *ptr;
int i;
struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload;
+ struct wmi_ns_arp_offload_req *ns = &arvif->ns_offload;
struct wmi_ns_offload *ns_tuple;
struct wmi_arp_offload *arp_tuple;
len = sizeof(*cmd) + sizeof(*tlv) +
- sizeof(*tlv) + WMI_MAX_NS_OFFLOADS *
+ sizeof(*tlv) + WMI_NS_ARP_OFFLOAD *
(sizeof(struct wmi_ns_offload) + sizeof(*tlv)) +
- sizeof(*tlv) + WMI_MAX_ARP_OFFLOADS *
+ sizeof(*tlv) + WMI_NS_ARP_OFFLOAD *
(sizeof(struct wmi_arp_offload) + sizeof(*tlv));
skb = ath10k_wmi_alloc_skb(ar, len);
@@ -3147,33 +3148,49 @@ ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar,
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 *
+ tlv->len = __cpu_to_le16(WMI_NS_ARP_OFFLOAD *
(sizeof(struct wmi_ns_offload) + sizeof(*tlv)));
ptr += sizeof(*tlv);
tlv = ptr;
- for (i = 0; i < WMI_MAX_NS_OFFLOADS; i++) {
+ for (i = 0; i < WMI_NS_ARP_OFFLOAD; 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 = (struct wmi_ns_offload *)tlv->value;
- ns_tuple->flags |= __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE);
+ if (ns->enable_offload) {
+ ns_tuple->flags |=
+ __cpu_to_le32(WMI_ARP_NS_OFF_FLAGS_VALID);
+ if (ns->info.target_addr_valid.s6_addr[i]) {
+ memcpy(&ns_tuple->target_ipaddr[0],
+ &ns->info.target_addr[i],
+ sizeof(struct in6_addr));
+ }
+ memcpy(&ns_tuple->solicitation_ipaddr,
+ &ns->info.self_addr[i], sizeof(struct in6_addr));
+ if (ns->info.target_ipv6_ac.s6_addr[i] == IPV6_ADDR_ANY)
+ ns_tuple->flags |=
+ __cpu_to_le32(WMI_NSOFF_IPV6_ANYCAST);
+ } else {
+ ns_tuple->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 *
+ tlv->len = __cpu_to_le16(WMI_NS_ARP_OFFLOAD *
(sizeof(struct wmi_arp_offload) + sizeof(*tlv)));
ptr += sizeof(*tlv);
tlv = ptr;
- for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) {
+ for (i = 0; i < WMI_NS_ARP_OFFLOAD; 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 = (struct wmi_arp_offload *)tlv->value;
if (arp->enable_offload && (i == 0)) {
arp_tuple->flags |=
- __cpu_to_le32(WMI_ARPOFF_FLAGS_VALID);
+ __cpu_to_le32(WMI_ARP_NS_OFF_FLAGS_VALID);
memcpy(&arp_tuple->target_ipaddr,
&arp->params.ipv4_addr,
sizeof(arp_tuple->target_ipaddr));
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 57b81b8bae82..291969db777d 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <net/mac80211.h>
#include <linux/ipv6.h>
+#include <net/ipv6.h>
#include <linux/in.h>
/*
@@ -2887,13 +2888,12 @@ struct wmi_start_scan_common {
} __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_NS_ARP_OFFLOAD 2
+#define WMI_ARP_NS_OFF_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
+#define WMI_NSOFF_IPV6_ANYCAST BIT(3)
struct wmi_ns_offload_info {
struct in6_addr src_addr;
@@ -2902,7 +2902,7 @@ struct wmi_ns_offload_info {
struct wmi_mac_addr self_macaddr;
u8 src_ipv6_addr_valid;
struct in6_addr target_addr_valid;
- struct in6_addr target_addr_ac_type;
+ struct in6_addr target_ipv6_ac;
u8 slot_idx;
} __packed;
@@ -2914,13 +2914,13 @@ struct wmi_ns_arp_offload_req {
struct in_addr ipv4_addr;
struct in6_addr ipv6_addr;
} params;
- struct wmi_ns_offload_info offload_info;
+ struct wmi_ns_offload_info 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 target_ipaddr[WMI_NS_ARP_OFFLOAD];
struct in6_addr solicitation_ipaddr;
struct in6_addr remote_ipaddr;
struct wmi_mac_addr target_mac;
diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c
index 262a1a19196e..aa18c2f1136b 100644
--- a/drivers/net/wireless/ath/ath10k/wow.c
+++ b/drivers/net/wireless/ath/ath10k/wow.c
@@ -17,6 +17,7 @@
#include "mac.h"
#include <net/mac80211.h>
+#include <net/addrconf.h>
#include "hif.h"
#include "core.h"
#include "debug.h"
@@ -232,6 +233,116 @@ static int ath10k_wow_wakeup(struct ath10k *ar)
}
static int
+ath10k_wow_fill_vdev_ns_offload_struct(struct ath10k_vif *arvif,
+ bool enable_offload)
+{
+ struct in6_addr addr[TARGET_NUM_STATIONS];
+ struct wmi_ns_arp_offload_req *ns;
+ struct wireless_dev *wdev;
+ struct inet6_dev *in6_dev;
+ struct in6_addr addr_type;
+ struct inet6_ifaddr *ifa;
+ struct ifacaddr6 *ifaca;
+ struct list_head *addr_list;
+ u32 scope, count = 0;
+ int i;
+
+ ns = &arvif->ns_offload;
+ if (!enable_offload) {
+ ns->offload_type = __cpu_to_le16(WMI_NS_ARP_OFFLOAD);
+ ns->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_DISABLE);
+ return 0;
+ }
+
+ wdev = ieee80211_vif_to_wdev(arvif->vif);
+ if (!wdev)
+ return -ENODEV;
+
+ in6_dev = __in6_dev_get(wdev->netdev);
+ if (!in6_dev)
+ return -ENODEV;
+
+ memset(&addr, 0, TARGET_NUM_STATIONS * sizeof(struct in6_addr));
+ memset(&addr_type, 0, sizeof(struct in6_addr));
+
+ /* Unicast Addresses */
+ read_lock_bh(&in6_dev->lock);
+ list_for_each(addr_list, &in6_dev->addr_list) {
+ if (count >= TARGET_NUM_STATIONS) {
+ read_unlock_bh(&in6_dev->lock);
+ return -EINVAL;
+ }
+
+ ifa = list_entry(addr_list, struct inet6_ifaddr, if_list);
+ if (ifa->flags & IFA_F_DADFAILED)
+ continue;
+ scope = ipv6_addr_src_scope(&ifa->addr);
+ switch (scope) {
+ case IPV6_ADDR_SCOPE_GLOBAL:
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ memcpy(&addr[count], &ifa->addr.s6_addr,
+ sizeof(ifa->addr.s6_addr));
+ addr_type.s6_addr[count] = IPV6_ADDR_UNICAST;
+ count += 1;
+ break;
+ }
+ }
+
+ /* Anycast Addresses */
+ for (ifaca = in6_dev->ac_list; ifaca; ifaca = ifaca->aca_next) {
+ if (count >= TARGET_NUM_STATIONS) {
+ read_unlock_bh(&in6_dev->lock);
+ return -EINVAL;
+ }
+
+ scope = ipv6_addr_src_scope(&ifaca->aca_addr);
+ switch (scope) {
+ case IPV6_ADDR_SCOPE_GLOBAL:
+ case IPV6_ADDR_SCOPE_LINKLOCAL:
+ memcpy(&addr[count], &ifaca->aca_addr,
+ sizeof(ifaca->aca_addr));
+ addr_type.s6_addr[count] = IPV6_ADDR_ANY;
+ count += 1;
+ break;
+ }
+ }
+ read_unlock_bh(&in6_dev->lock);
+
+ /* Filling up the request structure
+ * Filling the self_addr with solicited address
+ * A Solicited-Node multicast address is created by
+ * taking the last 24 bits of a unicast or anycast
+ * address and appending them to the prefix
+ *
+ * FF02:0000:0000:0000:0000:0001:FFXX:XXXX
+ *
+ * here XX is the unicast/anycast bits
+ */
+ for (i = 0; i < count; i++) {
+ ns->info.self_addr[i].s6_addr[0] = 0xFF;
+ ns->info.self_addr[i].s6_addr[1] = 0x02;
+ ns->info.self_addr[i].s6_addr[11] = 0x01;
+ ns->info.self_addr[i].s6_addr[12] = 0xFF;
+ ns->info.self_addr[i].s6_addr[13] = addr[i].s6_addr[13];
+ ns->info.self_addr[i].s6_addr[14] = addr[i].s6_addr[14];
+ ns->info.self_addr[i].s6_addr[15] = addr[i].s6_addr[15];
+ ns->info.slot_idx = i;
+ memcpy(&ns->info.target_addr[i], &addr[i],
+ sizeof(struct in6_addr));
+ ns->info.target_addr_valid.s6_addr[i] = 1;
+ ns->info.target_ipv6_ac.s6_addr[i] = addr_type.s6_addr[i];
+ memcpy(&ns->params.ipv6_addr, &ns->info.target_addr[i],
+ sizeof(struct in6_addr));
+ }
+
+ ns->offload_type = __cpu_to_le16(WMI_NS_ARP_OFFLOAD);
+ ns->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_ENABLE);
+ ns->num_ns_offload_count = __cpu_to_le16(count);
+
+ return 0;
+}
+
+static int
ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif,
bool enable_offload)
{
@@ -291,6 +402,13 @@ static int ath10k_wow_enable_ns_arp_offload(struct ath10k *ar, bool offload)
return ret;
}
+ ret = ath10k_wow_fill_vdev_ns_offload_struct(arvif, offload);
+ if (ret) {
+ ath10k_err(ar, "NS-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",