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