18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
108c2ecf20Sopenharmony_ci * Copyright(c) 2015 Intel Deutschland GmbH
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
138c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
148c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
178c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
198c2ecf20Sopenharmony_ci * General Public License for more details.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
228c2ecf20Sopenharmony_ci * in the file called COPYING.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Contact Information:
258c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
268c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * BSD LICENSE
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
318c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
328c2ecf20Sopenharmony_ci * Copyright(c) 2015 Intel Deutschland GmbH
338c2ecf20Sopenharmony_ci * All rights reserved.
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
368c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
378c2ecf20Sopenharmony_ci * are met:
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
408c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
418c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
428c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
438c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
448c2ecf20Sopenharmony_ci *    distribution.
458c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
468c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
478c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
508c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
518c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
528c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
538c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
548c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
558c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
568c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
578c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
588c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
598c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci *****************************************************************************/
628c2ecf20Sopenharmony_ci#include <net/ipv6.h>
638c2ecf20Sopenharmony_ci#include <net/addrconf.h>
648c2ecf20Sopenharmony_ci#include <linux/bitops.h>
658c2ecf20Sopenharmony_ci#include "mvm.h"
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_civoid iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
688c2ecf20Sopenharmony_ci				struct iwl_wowlan_config_cmd *cmd)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int i;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/*
738c2ecf20Sopenharmony_ci	 * For QoS counters, we store the one to use next, so subtract 0x10
748c2ecf20Sopenharmony_ci	 * since the uCode will add 0x10 *before* using the value while we
758c2ecf20Sopenharmony_ci	 * increment after using the value (i.e. store the next value to use).
768c2ecf20Sopenharmony_ci	 */
778c2ecf20Sopenharmony_ci	for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
788c2ecf20Sopenharmony_ci		u16 seq = mvm_ap_sta->tid_data[i].seq_number;
798c2ecf20Sopenharmony_ci		seq -= 0x10;
808c2ecf20Sopenharmony_ci		cmd->qos_seq[i] = cpu_to_le16(seq);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciint iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
858c2ecf20Sopenharmony_ci			       struct ieee80211_vif *vif,
868c2ecf20Sopenharmony_ci			       bool disable_offloading,
878c2ecf20Sopenharmony_ci			       bool offload_ns,
888c2ecf20Sopenharmony_ci			       u32 cmd_flags)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	union {
918c2ecf20Sopenharmony_ci		struct iwl_proto_offload_cmd_v1 v1;
928c2ecf20Sopenharmony_ci		struct iwl_proto_offload_cmd_v2 v2;
938c2ecf20Sopenharmony_ci		struct iwl_proto_offload_cmd_v3_small v3s;
948c2ecf20Sopenharmony_ci		struct iwl_proto_offload_cmd_v3_large v3l;
958c2ecf20Sopenharmony_ci	} cmd = {};
968c2ecf20Sopenharmony_ci	struct iwl_host_cmd hcmd = {
978c2ecf20Sopenharmony_ci		.id = PROT_OFFLOAD_CONFIG_CMD,
988c2ecf20Sopenharmony_ci		.flags = cmd_flags,
998c2ecf20Sopenharmony_ci		.data[0] = &cmd,
1008c2ecf20Sopenharmony_ci		.dataflags[0] = IWL_HCMD_DFL_DUP,
1018c2ecf20Sopenharmony_ci	};
1028c2ecf20Sopenharmony_ci	struct iwl_proto_offload_cmd_common *common;
1038c2ecf20Sopenharmony_ci	u32 enabled = 0, size;
1048c2ecf20Sopenharmony_ci	u32 capa_flags = mvm->fw->ucode_capa.flags;
1058c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
1068c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1078c2ecf20Sopenharmony_ci	int i;
1088c2ecf20Sopenharmony_ci	/*
1098c2ecf20Sopenharmony_ci	 * Skip tentative address when ns offload is enabled to avoid
1108c2ecf20Sopenharmony_ci	 * violating RFC4862.
1118c2ecf20Sopenharmony_ci	 * Keep tentative address when ns offload is disabled so the NS packets
1128c2ecf20Sopenharmony_ci	 * will not be filtered out and will wake up the host.
1138c2ecf20Sopenharmony_ci	 */
1148c2ecf20Sopenharmony_ci	bool skip_tentative = offload_ns;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
1178c2ecf20Sopenharmony_ci	    capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
1188c2ecf20Sopenharmony_ci		struct iwl_ns_config *nsc;
1198c2ecf20Sopenharmony_ci		struct iwl_targ_addr *addrs;
1208c2ecf20Sopenharmony_ci		int n_nsc, n_addrs;
1218c2ecf20Sopenharmony_ci		int c;
1228c2ecf20Sopenharmony_ci		int num_skipped = 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
1258c2ecf20Sopenharmony_ci			nsc = cmd.v3s.ns_config;
1268c2ecf20Sopenharmony_ci			n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
1278c2ecf20Sopenharmony_ci			addrs = cmd.v3s.targ_addrs;
1288c2ecf20Sopenharmony_ci			n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
1298c2ecf20Sopenharmony_ci		} else {
1308c2ecf20Sopenharmony_ci			nsc = cmd.v3l.ns_config;
1318c2ecf20Sopenharmony_ci			n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
1328c2ecf20Sopenharmony_ci			addrs = cmd.v3l.targ_addrs;
1338c2ecf20Sopenharmony_ci			n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		/*
1378c2ecf20Sopenharmony_ci		 * For each address we have (and that will fit) fill a target
1388c2ecf20Sopenharmony_ci		 * address struct and combine for NS offload structs with the
1398c2ecf20Sopenharmony_ci		 * solicited node addresses.
1408c2ecf20Sopenharmony_ci		 */
1418c2ecf20Sopenharmony_ci		for (i = 0, c = 0;
1428c2ecf20Sopenharmony_ci		     i < mvmvif->num_target_ipv6_addrs &&
1438c2ecf20Sopenharmony_ci		     i < n_addrs && c < n_nsc; i++) {
1448c2ecf20Sopenharmony_ci			struct in6_addr solicited_addr;
1458c2ecf20Sopenharmony_ci			int j;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci			if (skip_tentative &&
1488c2ecf20Sopenharmony_ci			    test_bit(i, mvmvif->tentative_addrs)) {
1498c2ecf20Sopenharmony_ci				num_skipped++;
1508c2ecf20Sopenharmony_ci				continue;
1518c2ecf20Sopenharmony_ci			}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci			addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
1548c2ecf20Sopenharmony_ci						  &solicited_addr);
1558c2ecf20Sopenharmony_ci			for (j = 0; j < c; j++)
1568c2ecf20Sopenharmony_ci				if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
1578c2ecf20Sopenharmony_ci						  &solicited_addr) == 0)
1588c2ecf20Sopenharmony_ci					break;
1598c2ecf20Sopenharmony_ci			if (j == c)
1608c2ecf20Sopenharmony_ci				c++;
1618c2ecf20Sopenharmony_ci			addrs[i].addr = mvmvif->target_ipv6_addrs[i];
1628c2ecf20Sopenharmony_ci			addrs[i].config_num = cpu_to_le32(j);
1638c2ecf20Sopenharmony_ci			nsc[j].dest_ipv6_addr = solicited_addr;
1648c2ecf20Sopenharmony_ci			memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		if (mvmvif->num_target_ipv6_addrs - num_skipped)
1688c2ecf20Sopenharmony_ci			enabled |= IWL_D3_PROTO_IPV6_VALID;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
1718c2ecf20Sopenharmony_ci			cmd.v3s.num_valid_ipv6_addrs =
1728c2ecf20Sopenharmony_ci				cpu_to_le32(i - num_skipped);
1738c2ecf20Sopenharmony_ci		else
1748c2ecf20Sopenharmony_ci			cmd.v3l.num_valid_ipv6_addrs =
1758c2ecf20Sopenharmony_ci				cpu_to_le32(i - num_skipped);
1768c2ecf20Sopenharmony_ci	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
1778c2ecf20Sopenharmony_ci		bool found = false;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
1808c2ecf20Sopenharmony_ci			     sizeof(mvmvif->target_ipv6_addrs[0]));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
1838c2ecf20Sopenharmony_ci				    IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) {
1848c2ecf20Sopenharmony_ci			if (skip_tentative &&
1858c2ecf20Sopenharmony_ci			    test_bit(i, mvmvif->tentative_addrs))
1868c2ecf20Sopenharmony_ci				continue;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci			memcpy(cmd.v2.target_ipv6_addr[i],
1898c2ecf20Sopenharmony_ci			       &mvmvif->target_ipv6_addrs[i],
1908c2ecf20Sopenharmony_ci			       sizeof(cmd.v2.target_ipv6_addr[i]));
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci			found = true;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci		if (found) {
1958c2ecf20Sopenharmony_ci			enabled |= IWL_D3_PROTO_IPV6_VALID;
1968c2ecf20Sopenharmony_ci			memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
1978c2ecf20Sopenharmony_ci		}
1988c2ecf20Sopenharmony_ci	} else {
1998c2ecf20Sopenharmony_ci		bool found = false;
2008c2ecf20Sopenharmony_ci		BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
2018c2ecf20Sopenharmony_ci			     sizeof(mvmvif->target_ipv6_addrs[0]));
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
2048c2ecf20Sopenharmony_ci				    IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) {
2058c2ecf20Sopenharmony_ci			if (skip_tentative &&
2068c2ecf20Sopenharmony_ci			    test_bit(i, mvmvif->tentative_addrs))
2078c2ecf20Sopenharmony_ci				continue;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci			memcpy(cmd.v1.target_ipv6_addr[i],
2108c2ecf20Sopenharmony_ci			       &mvmvif->target_ipv6_addrs[i],
2118c2ecf20Sopenharmony_ci			       sizeof(cmd.v1.target_ipv6_addr[i]));
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			found = true;
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		if (found) {
2178c2ecf20Sopenharmony_ci			enabled |= IWL_D3_PROTO_IPV6_VALID;
2188c2ecf20Sopenharmony_ci			memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
2198c2ecf20Sopenharmony_ci		}
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID))
2238c2ecf20Sopenharmony_ci		enabled |= IWL_D3_PROTO_OFFLOAD_NS;
2248c2ecf20Sopenharmony_ci#endif
2258c2ecf20Sopenharmony_ci	if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
2268c2ecf20Sopenharmony_ci		common = &cmd.v3s.common;
2278c2ecf20Sopenharmony_ci		size = sizeof(cmd.v3s);
2288c2ecf20Sopenharmony_ci	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
2298c2ecf20Sopenharmony_ci		common = &cmd.v3l.common;
2308c2ecf20Sopenharmony_ci		size = sizeof(cmd.v3l);
2318c2ecf20Sopenharmony_ci	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
2328c2ecf20Sopenharmony_ci		common = &cmd.v2.common;
2338c2ecf20Sopenharmony_ci		size = sizeof(cmd.v2);
2348c2ecf20Sopenharmony_ci	} else {
2358c2ecf20Sopenharmony_ci		common = &cmd.v1.common;
2368c2ecf20Sopenharmony_ci		size = sizeof(cmd.v1);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (vif->bss_conf.arp_addr_cnt) {
2408c2ecf20Sopenharmony_ci		enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;
2418c2ecf20Sopenharmony_ci		common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
2428c2ecf20Sopenharmony_ci		memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (!disable_offloading)
2468c2ecf20Sopenharmony_ci		common->enabled = cpu_to_le32(enabled);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	hcmd.len[0] = size;
2498c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd(mvm, &hcmd);
2508c2ecf20Sopenharmony_ci}
251