1*8e99ea8dSJohannes Berg // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2*8e99ea8dSJohannes Berg /* 3*8e99ea8dSJohannes Berg * Copyright (C) 2012-2014 Intel Corporation 4*8e99ea8dSJohannes Berg * Copyright (C) 2013-2014 Intel Mobile Communications GmbH 5*8e99ea8dSJohannes Berg * Copyright (C) 2015 Intel Deutschland GmbH 6*8e99ea8dSJohannes Berg */ 7e705c121SKalle Valo #include <net/ipv6.h> 8e705c121SKalle Valo #include <net/addrconf.h> 9c97dab40SSara Sharon #include <linux/bitops.h> 10e705c121SKalle Valo #include "mvm.h" 11e705c121SKalle Valo 12e705c121SKalle Valo void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, 13e705c121SKalle Valo struct iwl_wowlan_config_cmd *cmd) 14e705c121SKalle Valo { 15e705c121SKalle Valo int i; 16e705c121SKalle Valo 17e705c121SKalle Valo /* 18e705c121SKalle Valo * For QoS counters, we store the one to use next, so subtract 0x10 19e705c121SKalle Valo * since the uCode will add 0x10 *before* using the value while we 20e705c121SKalle Valo * increment after using the value (i.e. store the next value to use). 21e705c121SKalle Valo */ 22e705c121SKalle Valo for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 23e705c121SKalle Valo u16 seq = mvm_ap_sta->tid_data[i].seq_number; 24e705c121SKalle Valo seq -= 0x10; 25e705c121SKalle Valo cmd->qos_seq[i] = cpu_to_le16(seq); 26e705c121SKalle Valo } 27e705c121SKalle Valo } 28e705c121SKalle Valo 29e705c121SKalle Valo int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, 30e705c121SKalle Valo struct ieee80211_vif *vif, 31e705c121SKalle Valo bool disable_offloading, 32c97dab40SSara Sharon bool offload_ns, 33e705c121SKalle Valo u32 cmd_flags) 34e705c121SKalle Valo { 35e705c121SKalle Valo union { 36e705c121SKalle Valo struct iwl_proto_offload_cmd_v1 v1; 37e705c121SKalle Valo struct iwl_proto_offload_cmd_v2 v2; 38e705c121SKalle Valo struct iwl_proto_offload_cmd_v3_small v3s; 39e705c121SKalle Valo struct iwl_proto_offload_cmd_v3_large v3l; 40e705c121SKalle Valo } cmd = {}; 41e705c121SKalle Valo struct iwl_host_cmd hcmd = { 42e705c121SKalle Valo .id = PROT_OFFLOAD_CONFIG_CMD, 43e705c121SKalle Valo .flags = cmd_flags, 44e705c121SKalle Valo .data[0] = &cmd, 45e705c121SKalle Valo .dataflags[0] = IWL_HCMD_DFL_DUP, 46e705c121SKalle Valo }; 47e705c121SKalle Valo struct iwl_proto_offload_cmd_common *common; 48e705c121SKalle Valo u32 enabled = 0, size; 49e705c121SKalle Valo u32 capa_flags = mvm->fw->ucode_capa.flags; 50e705c121SKalle Valo #if IS_ENABLED(CONFIG_IPV6) 51e705c121SKalle Valo struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); 52e705c121SKalle Valo int i; 53c97dab40SSara Sharon /* 54c97dab40SSara Sharon * Skip tentative address when ns offload is enabled to avoid 55c97dab40SSara Sharon * violating RFC4862. 56c97dab40SSara Sharon * Keep tentative address when ns offload is disabled so the NS packets 57c97dab40SSara Sharon * will not be filtered out and will wake up the host. 58c97dab40SSara Sharon */ 59c97dab40SSara Sharon bool skip_tentative = offload_ns; 60e705c121SKalle Valo 61e705c121SKalle Valo if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || 62e705c121SKalle Valo capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { 63e705c121SKalle Valo struct iwl_ns_config *nsc; 64e705c121SKalle Valo struct iwl_targ_addr *addrs; 65e705c121SKalle Valo int n_nsc, n_addrs; 66e705c121SKalle Valo int c; 67c97dab40SSara Sharon int num_skipped = 0; 68e705c121SKalle Valo 69e705c121SKalle Valo if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { 70e705c121SKalle Valo nsc = cmd.v3s.ns_config; 71e705c121SKalle Valo n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; 72e705c121SKalle Valo addrs = cmd.v3s.targ_addrs; 73e705c121SKalle Valo n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; 74e705c121SKalle Valo } else { 75e705c121SKalle Valo nsc = cmd.v3l.ns_config; 76e705c121SKalle Valo n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; 77e705c121SKalle Valo addrs = cmd.v3l.targ_addrs; 78e705c121SKalle Valo n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; 79e705c121SKalle Valo } 80e705c121SKalle Valo 81e705c121SKalle Valo /* 82e705c121SKalle Valo * For each address we have (and that will fit) fill a target 83e705c121SKalle Valo * address struct and combine for NS offload structs with the 84e705c121SKalle Valo * solicited node addresses. 85e705c121SKalle Valo */ 86e705c121SKalle Valo for (i = 0, c = 0; 87e705c121SKalle Valo i < mvmvif->num_target_ipv6_addrs && 88e705c121SKalle Valo i < n_addrs && c < n_nsc; i++) { 89e705c121SKalle Valo struct in6_addr solicited_addr; 90e705c121SKalle Valo int j; 91e705c121SKalle Valo 92c97dab40SSara Sharon if (skip_tentative && 93c97dab40SSara Sharon test_bit(i, mvmvif->tentative_addrs)) { 94c97dab40SSara Sharon num_skipped++; 95c97dab40SSara Sharon continue; 96c97dab40SSara Sharon } 97c97dab40SSara Sharon 98e705c121SKalle Valo addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], 99e705c121SKalle Valo &solicited_addr); 100e705c121SKalle Valo for (j = 0; j < c; j++) 101e705c121SKalle Valo if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, 102e705c121SKalle Valo &solicited_addr) == 0) 103e705c121SKalle Valo break; 104e705c121SKalle Valo if (j == c) 105e705c121SKalle Valo c++; 106e705c121SKalle Valo addrs[i].addr = mvmvif->target_ipv6_addrs[i]; 107e705c121SKalle Valo addrs[i].config_num = cpu_to_le32(j); 108e705c121SKalle Valo nsc[j].dest_ipv6_addr = solicited_addr; 109e705c121SKalle Valo memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); 110e705c121SKalle Valo } 111e705c121SKalle Valo 112c97dab40SSara Sharon if (mvmvif->num_target_ipv6_addrs - num_skipped) 113c97dab40SSara Sharon enabled |= IWL_D3_PROTO_IPV6_VALID; 114c97dab40SSara Sharon 115e705c121SKalle Valo if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) 116c97dab40SSara Sharon cmd.v3s.num_valid_ipv6_addrs = 117c97dab40SSara Sharon cpu_to_le32(i - num_skipped); 118e705c121SKalle Valo else 119c97dab40SSara Sharon cmd.v3l.num_valid_ipv6_addrs = 120c97dab40SSara Sharon cpu_to_le32(i - num_skipped); 121e705c121SKalle Valo } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { 122c97dab40SSara Sharon bool found = false; 123e705c121SKalle Valo 124e705c121SKalle Valo BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != 125e705c121SKalle Valo sizeof(mvmvif->target_ipv6_addrs[0])); 126e705c121SKalle Valo 127e705c121SKalle Valo for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, 128c97dab40SSara Sharon IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) { 129c97dab40SSara Sharon if (skip_tentative && 130c97dab40SSara Sharon test_bit(i, mvmvif->tentative_addrs)) 131c97dab40SSara Sharon continue; 132c97dab40SSara Sharon 133e705c121SKalle Valo memcpy(cmd.v2.target_ipv6_addr[i], 134e705c121SKalle Valo &mvmvif->target_ipv6_addrs[i], 135e705c121SKalle Valo sizeof(cmd.v2.target_ipv6_addr[i])); 136e705c121SKalle Valo 137c97dab40SSara Sharon found = true; 138c97dab40SSara Sharon } 139c97dab40SSara Sharon if (found) { 140c97dab40SSara Sharon enabled |= IWL_D3_PROTO_IPV6_VALID; 141c97dab40SSara Sharon memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); 142c97dab40SSara Sharon } 143c97dab40SSara Sharon } else { 144c97dab40SSara Sharon bool found = false; 145e705c121SKalle Valo BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != 146e705c121SKalle Valo sizeof(mvmvif->target_ipv6_addrs[0])); 147e705c121SKalle Valo 148e705c121SKalle Valo for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, 149c97dab40SSara Sharon IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) { 150c97dab40SSara Sharon if (skip_tentative && 151c97dab40SSara Sharon test_bit(i, mvmvif->tentative_addrs)) 152c97dab40SSara Sharon continue; 153c97dab40SSara Sharon 154e705c121SKalle Valo memcpy(cmd.v1.target_ipv6_addr[i], 155e705c121SKalle Valo &mvmvif->target_ipv6_addrs[i], 156e705c121SKalle Valo sizeof(cmd.v1.target_ipv6_addr[i])); 157e705c121SKalle Valo 158c97dab40SSara Sharon found = true; 159c97dab40SSara Sharon } 160c97dab40SSara Sharon 161c97dab40SSara Sharon if (found) { 162c97dab40SSara Sharon enabled |= IWL_D3_PROTO_IPV6_VALID; 163c97dab40SSara Sharon memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); 164c97dab40SSara Sharon } 165c97dab40SSara Sharon } 166c97dab40SSara Sharon 167c97dab40SSara Sharon if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID)) 168c97dab40SSara Sharon enabled |= IWL_D3_PROTO_OFFLOAD_NS; 169c97dab40SSara Sharon #endif 170e705c121SKalle Valo if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { 171e705c121SKalle Valo common = &cmd.v3s.common; 172e705c121SKalle Valo size = sizeof(cmd.v3s); 173e705c121SKalle Valo } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { 174e705c121SKalle Valo common = &cmd.v3l.common; 175e705c121SKalle Valo size = sizeof(cmd.v3l); 176e705c121SKalle Valo } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { 177e705c121SKalle Valo common = &cmd.v2.common; 178e705c121SKalle Valo size = sizeof(cmd.v2); 179e705c121SKalle Valo } else { 180e705c121SKalle Valo common = &cmd.v1.common; 181e705c121SKalle Valo size = sizeof(cmd.v1); 182e705c121SKalle Valo } 183e705c121SKalle Valo 184e705c121SKalle Valo if (vif->bss_conf.arp_addr_cnt) { 185c97dab40SSara Sharon enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID; 186e705c121SKalle Valo common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; 187e705c121SKalle Valo memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); 188e705c121SKalle Valo } 189e705c121SKalle Valo 190e705c121SKalle Valo if (!disable_offloading) 191e705c121SKalle Valo common->enabled = cpu_to_le32(enabled); 192e705c121SKalle Valo 193e705c121SKalle Valo hcmd.len[0] = size; 194e705c121SKalle Valo return iwl_mvm_send_cmd(mvm, &hcmd); 195e705c121SKalle Valo } 196