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