linux/drivers/net/wireless/intel/iwlwifi/mvm/offloading.c
<<
>>
Prefs
   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
  67void 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
  84int 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