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