1/****************************************************************************** 2 * 3 * This file is provided under a dual BSD/GPLv2 license. When using or 4 * redistributing this file, you may do so under either license. 5 * 6 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 9 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of version 2 of the GNU General Public License as 13 * published by the Free Software Foundation. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 23 * USA 24 * 25 * The full GNU General Public License is included in this distribution 26 * in the file called COPYING. 27 * 28 * Contact Information: 29 * Intel Linux Wireless <ilw@linux.intel.com> 30 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 31 * 32 * BSD LICENSE 33 * 34 * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. 35 * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH 36 * All rights reserved. 37 * 38 * Redistribution and use in source and binary forms, with or without 39 * modification, are permitted provided that the following conditions 40 * are met: 41 * 42 * * Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * * Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in 46 * the documentation and/or other materials provided with the 47 * distribution. 48 * * Neither the name Intel Corporation nor the names of its 49 * contributors may be used to endorse or promote products derived 50 * from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 53 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 54 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 55 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 56 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 57 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 58 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 59 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 60 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 61 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 62 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 * 64 *****************************************************************************/ 65#include <net/ipv6.h> 66#include <net/addrconf.h> 67#include "mvm.h" 68 69void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, 70 struct iwl_wowlan_config_cmd_v2 *cmd) 71{ 72 int i; 73 74 /* 75 * For QoS counters, we store the one to use next, so subtract 0x10 76 * since the uCode will add 0x10 *before* using the value while we 77 * increment after using the value (i.e. store the next value to use). 78 */ 79 for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 80 u16 seq = mvm_ap_sta->tid_data[i].seq_number; 81 seq -= 0x10; 82 cmd->qos_seq[i] = cpu_to_le16(seq); 83 } 84} 85 86int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, 87 struct ieee80211_vif *vif, 88 bool disable_offloading, 89 u32 cmd_flags) 90{ 91 union { 92 struct iwl_proto_offload_cmd_v1 v1; 93 struct iwl_proto_offload_cmd_v2 v2; 94 struct iwl_proto_offload_cmd_v3_small v3s; 95 struct iwl_proto_offload_cmd_v3_large v3l; 96 } cmd = {}; 97 struct iwl_host_cmd hcmd = { 98 .id = PROT_OFFLOAD_CONFIG_CMD, 99 .flags = cmd_flags, 100 .data[0] = &cmd, 101 .dataflags[0] = IWL_HCMD_DFL_DUP, 102 }; 103 struct iwl_proto_offload_cmd_common *common; 104 u32 enabled = 0, size; 105 u32 capa_flags = mvm->fw->ucode_capa.flags; 106#if IS_ENABLED(CONFIG_IPV6) 107 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 108 int i; 109 110 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || 111 capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { 112 struct iwl_ns_config *nsc; 113 struct iwl_targ_addr *addrs; 114 int n_nsc, n_addrs; 115 int c; 116 117 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { 118 nsc = cmd.v3s.ns_config; 119 n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; 120 addrs = cmd.v3s.targ_addrs; 121 n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; 122 } else { 123 nsc = cmd.v3l.ns_config; 124 n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; 125 addrs = cmd.v3l.targ_addrs; 126 n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; 127 } 128 129 if (mvmvif->num_target_ipv6_addrs) 130 enabled |= IWL_D3_PROTO_OFFLOAD_NS; 131 132 /* 133 * For each address we have (and that will fit) fill a target 134 * address struct and combine for NS offload structs with the 135 * solicited node addresses. 136 */ 137 for (i = 0, c = 0; 138 i < mvmvif->num_target_ipv6_addrs && 139 i < n_addrs && c < n_nsc; i++) { 140 struct in6_addr solicited_addr; 141 int j; 142 143 addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], 144 &solicited_addr); 145 for (j = 0; j < c; j++) 146 if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, 147 &solicited_addr) == 0) 148 break; 149 if (j == c) 150 c++; 151 addrs[i].addr = mvmvif->target_ipv6_addrs[i]; 152 addrs[i].config_num = cpu_to_le32(j); 153 nsc[j].dest_ipv6_addr = solicited_addr; 154 memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); 155 } 156 157 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) 158 cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); 159 else 160 cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); 161 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { 162 if (mvmvif->num_target_ipv6_addrs) { 163 enabled |= IWL_D3_PROTO_OFFLOAD_NS; 164 memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); 165 } 166 167 BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != 168 sizeof(mvmvif->target_ipv6_addrs[0])); 169 170 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, 171 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) 172 memcpy(cmd.v2.target_ipv6_addr[i], 173 &mvmvif->target_ipv6_addrs[i], 174 sizeof(cmd.v2.target_ipv6_addr[i])); 175 } else { 176 if (mvmvif->num_target_ipv6_addrs) { 177 enabled |= IWL_D3_PROTO_OFFLOAD_NS; 178 memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); 179 } 180 181 BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != 182 sizeof(mvmvif->target_ipv6_addrs[0])); 183 184 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, 185 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) 186 memcpy(cmd.v1.target_ipv6_addr[i], 187 &mvmvif->target_ipv6_addrs[i], 188 sizeof(cmd.v1.target_ipv6_addr[i])); 189 } 190#endif 191 192 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { 193 common = &cmd.v3s.common; 194 size = sizeof(cmd.v3s); 195 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { 196 common = &cmd.v3l.common; 197 size = sizeof(cmd.v3l); 198 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { 199 common = &cmd.v2.common; 200 size = sizeof(cmd.v2); 201 } else { 202 common = &cmd.v1.common; 203 size = sizeof(cmd.v1); 204 } 205 206 if (vif->bss_conf.arp_addr_cnt) { 207 enabled |= IWL_D3_PROTO_OFFLOAD_ARP; 208 common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; 209 memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); 210 } 211 212 if (!disable_offloading) 213 common->enabled = cpu_to_le32(enabled); 214 215 hcmd.len[0] = size; 216 return iwl_mvm_send_cmd(mvm, &hcmd); 217} 218