Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 /*
0003  * Copyright (C) 2012-2014, 2021-2022 Intel Corporation
0004  * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
0005  * Copyright (C) 2015 Intel Deutschland GmbH
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      * For QoS counters, we store the one to use next, so subtract 0x10
0019      * since the uCode will add 0x10 *before* using the value while we
0020      * increment after using the value (i.e. store the next value to use).
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      * Skip tentative address when ns offload is enabled to avoid
0057      * violating RFC4862.
0058      * Keep tentative address when ns offload is disabled so the NS packets
0059      * will not be filtered out and will wake up the host.
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          * For each address we have (and that will fit) fill a target
0085          * address struct and combine for NS offload structs with the
0086          * solicited node addresses.
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              * This basically uses iwl_proto_offload_cmd_v3_large
0181              * which doesn't have the sta_id parameter before the
0182              * common part.
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 }