0001
0002
0003
0004
0005
0006
0007 #include <net/ipv6.h>
0008 #include <net/addrconf.h>
0009 #include <linux/bitops.h>
0010 #include "mvm.h"
0011
0012 void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
0013 struct iwl_wowlan_config_cmd *cmd)
0014 {
0015 int i;
0016
0017
0018
0019
0020
0021
0022 for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
0023 u16 seq = mvm_ap_sta->tid_data[i].seq_number;
0024 seq -= 0x10;
0025 cmd->qos_seq[i] = cpu_to_le16(seq);
0026 }
0027 }
0028
0029 int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
0030 struct ieee80211_vif *vif,
0031 bool disable_offloading,
0032 bool offload_ns,
0033 u32 cmd_flags)
0034 {
0035 union {
0036 struct iwl_proto_offload_cmd_v1 v1;
0037 struct iwl_proto_offload_cmd_v2 v2;
0038 struct iwl_proto_offload_cmd_v3_small v3s;
0039 struct iwl_proto_offload_cmd_v4 v4;
0040 } cmd = {};
0041 struct iwl_host_cmd hcmd = {
0042 .id = PROT_OFFLOAD_CONFIG_CMD,
0043 .flags = cmd_flags,
0044 .data[0] = &cmd,
0045 .dataflags[0] = IWL_HCMD_DFL_DUP,
0046 };
0047 struct iwl_proto_offload_cmd_common *common;
0048 u32 enabled = 0, size;
0049 u32 capa_flags = mvm->fw->ucode_capa.flags;
0050 int ver = iwl_fw_lookup_cmd_ver(mvm->fw, hcmd.id, 0);
0051
0052 #if IS_ENABLED(CONFIG_IPV6)
0053 struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
0054 int i;
0055
0056
0057
0058
0059
0060
0061 bool skip_tentative = offload_ns;
0062
0063 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
0064 capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
0065 struct iwl_ns_config *nsc;
0066 struct iwl_targ_addr *addrs;
0067 int n_nsc, n_addrs;
0068 int c;
0069 int num_skipped = 0;
0070
0071 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
0072 nsc = cmd.v3s.ns_config;
0073 n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
0074 addrs = cmd.v3s.targ_addrs;
0075 n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
0076 } else {
0077 nsc = cmd.v4.ns_config;
0078 n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
0079 addrs = cmd.v4.targ_addrs;
0080 n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
0081 }
0082
0083
0084
0085
0086
0087
0088 for (i = 0, c = 0;
0089 i < mvmvif->num_target_ipv6_addrs &&
0090 i < n_addrs && c < n_nsc; i++) {
0091 struct in6_addr solicited_addr;
0092 int j;
0093
0094 if (skip_tentative &&
0095 test_bit(i, mvmvif->tentative_addrs)) {
0096 num_skipped++;
0097 continue;
0098 }
0099
0100 addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
0101 &solicited_addr);
0102 for (j = 0; j < c; j++)
0103 if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
0104 &solicited_addr) == 0)
0105 break;
0106 if (j == c)
0107 c++;
0108 addrs[i].addr = mvmvif->target_ipv6_addrs[i];
0109 addrs[i].config_num = cpu_to_le32(j);
0110 nsc[j].dest_ipv6_addr = solicited_addr;
0111 memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
0112 }
0113
0114 if (mvmvif->num_target_ipv6_addrs - num_skipped)
0115 enabled |= IWL_D3_PROTO_IPV6_VALID;
0116
0117 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
0118 cmd.v3s.num_valid_ipv6_addrs =
0119 cpu_to_le32(i - num_skipped);
0120 else
0121 cmd.v4.num_valid_ipv6_addrs =
0122 cpu_to_le32(i - num_skipped);
0123 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
0124 bool found = false;
0125
0126 BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
0127 sizeof(mvmvif->target_ipv6_addrs[0]));
0128
0129 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
0130 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) {
0131 if (skip_tentative &&
0132 test_bit(i, mvmvif->tentative_addrs))
0133 continue;
0134
0135 memcpy(cmd.v2.target_ipv6_addr[i],
0136 &mvmvif->target_ipv6_addrs[i],
0137 sizeof(cmd.v2.target_ipv6_addr[i]));
0138
0139 found = true;
0140 }
0141 if (found) {
0142 enabled |= IWL_D3_PROTO_IPV6_VALID;
0143 memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
0144 }
0145 } else {
0146 bool found = false;
0147 BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
0148 sizeof(mvmvif->target_ipv6_addrs[0]));
0149
0150 for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
0151 IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) {
0152 if (skip_tentative &&
0153 test_bit(i, mvmvif->tentative_addrs))
0154 continue;
0155
0156 memcpy(cmd.v1.target_ipv6_addr[i],
0157 &mvmvif->target_ipv6_addrs[i],
0158 sizeof(cmd.v1.target_ipv6_addr[i]));
0159
0160 found = true;
0161 }
0162
0163 if (found) {
0164 enabled |= IWL_D3_PROTO_IPV6_VALID;
0165 memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
0166 }
0167 }
0168
0169 if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID))
0170 enabled |= IWL_D3_PROTO_OFFLOAD_NS;
0171 #endif
0172 if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
0173 common = &cmd.v3s.common;
0174 size = sizeof(cmd.v3s);
0175 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
0176 common = &cmd.v4.common;
0177 size = sizeof(cmd.v4);
0178 if (ver < 4) {
0179
0180
0181
0182
0183
0184 size -= sizeof(cmd.v4.sta_id);
0185 hcmd.data[0] = common;
0186 }
0187 } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
0188 common = &cmd.v2.common;
0189 size = sizeof(cmd.v2);
0190 } else {
0191 common = &cmd.v1.common;
0192 size = sizeof(cmd.v1);
0193 }
0194
0195 if (vif->cfg.arp_addr_cnt) {
0196 enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;
0197 common->host_ipv4_addr = vif->cfg.arp_addr_list[0];
0198 memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
0199 }
0200
0201 if (!disable_offloading)
0202 common->enabled = cpu_to_le32(enabled);
0203
0204 hcmd.len[0] = size;
0205 return iwl_mvm_send_cmd(mvm, &hcmd);
0206 }