Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9368586e authored by Sarada Prasanna Garnayak's avatar Sarada Prasanna Garnayak Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarSarada Prasanna Garnayak <sgarna@codeaurora.org>
parent ef00227c
Loading
Loading
Loading
Loading
+25 −8
Original line number Diff line number Diff line
@@ -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));
+7 −7
Original line number Diff line number Diff line
@@ -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;
+118 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "mac.h"

#include <net/mac80211.h>
#include <net/addrconf.h>
#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",