From 9368586ebe6e622b57a153116be8357b3a524f9e Mon Sep 17 00:00:00 2001 From: Sarada Prasanna Garnayak Date: Fri, 5 Jan 2018 16:13:20 +0530 Subject: [PATCH] ath10k: enable neighbor solicitation offload in wow suspend In IPV6 the neighbor solicitations are used by nodes to determine the link layer address of a neighbor and neighbor advertisements are used by nodes to respond to a neighbor solicitation message. During WOW suspend offload the neighbor solicitation packet handling method to WLAN firmware, this adds support to prevent the device wakeup by neighbor solicitation packet. CRs-Fixed: 2217590 Change-Id: I5d860e6651b7c62223d9d00886fc386dcf647cab Signed-off-by: Sarada Prasanna Garnayak --- drivers/net/wireless/ath/ath10k/wmi-tlv.c | 33 ++++-- drivers/net/wireless/ath/ath10k/wmi.h | 14 +-- drivers/net/wireless/ath/ath10k/wow.c | 118 ++++++++++++++++++++++ 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 #include #include +#include #include /* @@ -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 +#include #include "hif.h" #include "core.h" #include "debug.h" @@ -231,6 +232,116 @@ static int ath10k_wow_wakeup(struct ath10k *ar) return 0; } +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",