Merge "ath10k: enable neighbor solicitation offload in wow suspend"

This commit is contained in:
Linux Build Service Account 2018-04-11 19:18:41 -07:00 committed by Gerrit - the friendly Code Review server
commit e718c5461b
3 changed files with 150 additions and 15 deletions

View file

@ -3123,13 +3123,14 @@ ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar,
void *ptr; void *ptr;
int i; int i;
struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload; 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_ns_offload *ns_tuple;
struct wmi_arp_offload *arp_tuple; struct wmi_arp_offload *arp_tuple;
len = sizeof(*cmd) + sizeof(*tlv) + 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(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)); (sizeof(struct wmi_arp_offload) + sizeof(*tlv));
skb = ath10k_wmi_alloc_skb(ar, len); 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)); ptr += (sizeof(*tlv) + sizeof(*cmd));
tlv = ptr; tlv = ptr;
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); 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))); (sizeof(struct wmi_ns_offload) + sizeof(*tlv)));
ptr += sizeof(*tlv); ptr += sizeof(*tlv);
tlv = ptr; 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->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE);
tlv->len = __cpu_to_le16(sizeof(struct wmi_ns_offload)); tlv->len = __cpu_to_le16(sizeof(struct wmi_ns_offload));
ns_tuple = (struct wmi_ns_offload *)tlv->value; 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)); ptr += (sizeof(*tlv) + sizeof(struct wmi_ns_offload));
tlv = ptr; tlv = ptr;
} }
tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); 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))); (sizeof(struct wmi_arp_offload) + sizeof(*tlv)));
ptr += sizeof(*tlv); ptr += sizeof(*tlv);
tlv = ptr; 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->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE);
tlv->len = __cpu_to_le16(sizeof(struct wmi_arp_offload)); tlv->len = __cpu_to_le16(sizeof(struct wmi_arp_offload));
arp_tuple = (struct wmi_arp_offload *)tlv->value; arp_tuple = (struct wmi_arp_offload *)tlv->value;
if (arp->enable_offload && (i == 0)) { if (arp->enable_offload && (i == 0)) {
arp_tuple->flags |= arp_tuple->flags |=
__cpu_to_le32(WMI_ARPOFF_FLAGS_VALID); __cpu_to_le32(WMI_ARP_NS_OFF_FLAGS_VALID);
memcpy(&arp_tuple->target_ipaddr, memcpy(&arp_tuple->target_ipaddr,
&arp->params.ipv4_addr, &arp->params.ipv4_addr,
sizeof(arp_tuple->target_ipaddr)); sizeof(arp_tuple->target_ipaddr));

View file

@ -21,6 +21,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <net/ipv6.h>
#include <linux/in.h> #include <linux/in.h>
/* /*
@ -2887,13 +2888,12 @@ struct wmi_start_scan_common {
} __packed; } __packed;
/* ARP-NS offload data structure */ /* ARP-NS offload data structure */
#define WMI_NSOFF_MAX_TARGET_IPS 2 #define WMI_NS_ARP_OFFLOAD 2
#define WMI_MAX_NS_OFFLOADS 2 #define WMI_ARP_NS_OFF_FLAGS_VALID BIT(0)
#define WMI_MAX_ARP_OFFLOADS 2
#define WMI_ARPOFF_FLAGS_VALID BIT(0)
#define WMI_IPV4_ARP_REPLY_OFFLOAD 0 #define WMI_IPV4_ARP_REPLY_OFFLOAD 0
#define WMI_ARP_NS_OFFLOAD_DISABLE 0 #define WMI_ARP_NS_OFFLOAD_DISABLE 0
#define WMI_ARP_NS_OFFLOAD_ENABLE 1 #define WMI_ARP_NS_OFFLOAD_ENABLE 1
#define WMI_NSOFF_IPV6_ANYCAST BIT(3)
struct wmi_ns_offload_info { struct wmi_ns_offload_info {
struct in6_addr src_addr; struct in6_addr src_addr;
@ -2902,7 +2902,7 @@ struct wmi_ns_offload_info {
struct wmi_mac_addr self_macaddr; struct wmi_mac_addr self_macaddr;
u8 src_ipv6_addr_valid; u8 src_ipv6_addr_valid;
struct in6_addr target_addr_valid; struct in6_addr target_addr_valid;
struct in6_addr target_addr_ac_type; struct in6_addr target_ipv6_ac;
u8 slot_idx; u8 slot_idx;
} __packed; } __packed;
@ -2914,13 +2914,13 @@ struct wmi_ns_arp_offload_req {
struct in_addr ipv4_addr; struct in_addr ipv4_addr;
struct in6_addr ipv6_addr; struct in6_addr ipv6_addr;
} params; } params;
struct wmi_ns_offload_info offload_info; struct wmi_ns_offload_info info;
struct wmi_mac_addr bssid; struct wmi_mac_addr bssid;
} __packed; } __packed;
struct wmi_ns_offload { struct wmi_ns_offload {
__le32 flags; __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 solicitation_ipaddr;
struct in6_addr remote_ipaddr; struct in6_addr remote_ipaddr;
struct wmi_mac_addr target_mac; struct wmi_mac_addr target_mac;

View file

@ -17,6 +17,7 @@
#include "mac.h" #include "mac.h"
#include <net/mac80211.h> #include <net/mac80211.h>
#include <net/addrconf.h>
#include "hif.h" #include "hif.h"
#include "core.h" #include "core.h"
#include "debug.h" #include "debug.h"
@ -231,6 +232,116 @@ static int ath10k_wow_wakeup(struct ath10k *ar)
return 0; 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 static int
ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif, ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif,
bool enable_offload) bool enable_offload)
@ -291,6 +402,13 @@ static int ath10k_wow_enable_ns_arp_offload(struct ath10k *ar, bool offload)
return ret; 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); ret = ath10k_wmi_set_arp_ns_offload(ar, arvif);
if (ret) { if (ret) {
ath10k_err(ar, "failed to send offload cmd, vdev: %d\n", ath10k_err(ar, "failed to send offload cmd, vdev: %d\n",