Path: blob/main/sys/contrib/dev/iwlwifi/mvm/offloading.c
48287 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2012-2014, 2021-2022, 2024 Intel Corporation3* Copyright (C) 2013-2014 Intel Mobile Communications GmbH4* Copyright (C) 2015 Intel Deutschland GmbH5*/6#include <net/ipv6.h>7#include <net/addrconf.h>8#include <linux/bitops.h>9#include "mvm.h"1011void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,12struct iwl_wowlan_config_cmd_v6 *cmd)13{14int i;1516/*17* For QoS counters, we store the one to use next, so subtract 0x1018* since the uCode will add 0x10 *before* using the value while we19* increment after using the value (i.e. store the next value to use).20*/21for (i = 0; i < IWL_MAX_TID_COUNT; i++) {22u16 seq = mvm_ap_sta->tid_data[i].seq_number;23seq -= 0x10;24cmd->qos_seq[i] = cpu_to_le16(seq);25}26}2728int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,29struct ieee80211_vif *vif,30bool disable_offloading,31bool offload_ns,32u32 cmd_flags,33u8 sta_id)34{35union {36struct iwl_proto_offload_cmd_v1 v1;37struct iwl_proto_offload_cmd_v2 v2;38struct iwl_proto_offload_cmd_v3_small v3s;39struct iwl_proto_offload_cmd_v4 v4;40} cmd = {};41struct iwl_host_cmd hcmd = {42.id = PROT_OFFLOAD_CONFIG_CMD,43.flags = cmd_flags,44.data[0] = &cmd,45.dataflags[0] = IWL_HCMD_DFL_DUP,46};47struct iwl_proto_offload_cmd_common *common;48u32 enabled = 0, size;49u32 capa_flags = mvm->fw->ucode_capa.flags;50int ver = iwl_fw_lookup_cmd_ver(mvm->fw, hcmd.id, 0);5152#if IS_ENABLED(CONFIG_IPV6)53struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);54int i;55/*56* Skip tentative address when ns offload is enabled to avoid57* violating RFC4862.58* Keep tentative address when ns offload is disabled so the NS packets59* will not be filtered out and will wake up the host.60*/61bool skip_tentative = offload_ns;6263if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||64capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {65struct iwl_ns_config *nsc;66struct iwl_targ_addr *addrs;67int n_nsc, n_addrs;68int c;69int num_skipped = 0;7071if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {72nsc = cmd.v3s.ns_config;73n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;74addrs = cmd.v3s.targ_addrs;75n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;76} else {77nsc = cmd.v4.ns_config;78n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;79addrs = cmd.v4.targ_addrs;80n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;81}8283/*84* For each address we have (and that will fit) fill a target85* address struct and combine for NS offload structs with the86* solicited node addresses.87*/88for (i = 0, c = 0;89i < mvmvif->num_target_ipv6_addrs &&90i < n_addrs && c < n_nsc; i++) {91struct in6_addr solicited_addr;92int j;9394if (skip_tentative &&95test_bit(i, mvmvif->tentative_addrs)) {96num_skipped++;97continue;98}99100addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],101&solicited_addr);102for (j = 0; j < c; j++)103if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,104&solicited_addr) == 0)105break;106if (j == c)107c++;108addrs[i].addr = mvmvif->target_ipv6_addrs[i];109addrs[i].config_num = cpu_to_le32(j);110nsc[j].dest_ipv6_addr = solicited_addr;111memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);112}113114if (mvmvif->num_target_ipv6_addrs - num_skipped)115enabled |= IWL_D3_PROTO_IPV6_VALID;116117if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)118cmd.v3s.num_valid_ipv6_addrs =119cpu_to_le32(i - num_skipped);120else121cmd.v4.num_valid_ipv6_addrs =122cpu_to_le32(i - num_skipped);123} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {124bool found = false;125126BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=127sizeof(mvmvif->target_ipv6_addrs[0]));128129for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,130IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) {131if (skip_tentative &&132test_bit(i, mvmvif->tentative_addrs))133continue;134135memcpy(cmd.v2.target_ipv6_addr[i],136&mvmvif->target_ipv6_addrs[i],137sizeof(cmd.v2.target_ipv6_addr[i]));138139found = true;140}141if (found) {142enabled |= IWL_D3_PROTO_IPV6_VALID;143memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);144}145} else {146bool found = false;147BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=148sizeof(mvmvif->target_ipv6_addrs[0]));149150for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,151IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) {152if (skip_tentative &&153test_bit(i, mvmvif->tentative_addrs))154continue;155156memcpy(cmd.v1.target_ipv6_addr[i],157&mvmvif->target_ipv6_addrs[i],158sizeof(cmd.v1.target_ipv6_addr[i]));159160found = true;161}162163if (found) {164enabled |= IWL_D3_PROTO_IPV6_VALID;165memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);166}167}168169if (offload_ns && (enabled & IWL_D3_PROTO_IPV6_VALID))170enabled |= IWL_D3_PROTO_OFFLOAD_NS;171#endif172if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {173common = &cmd.v3s.common;174size = sizeof(cmd.v3s);175} else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {176common = &cmd.v4.common;177size = sizeof(cmd.v4);178if (ver < 4) {179/*180* This basically uses iwl_proto_offload_cmd_v3_large181* which doesn't have the sta_id parameter before the182* common part.183*/184size -= sizeof(cmd.v4.sta_id);185hcmd.data[0] = common;186}187} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {188common = &cmd.v2.common;189size = sizeof(cmd.v2);190} else {191common = &cmd.v1.common;192size = sizeof(cmd.v1);193}194195if (vif->cfg.arp_addr_cnt) {196enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID;197common->host_ipv4_addr = vif->cfg.arp_addr_list[0];198memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);199}200201if (fw_has_capa(&mvm->fw->ucode_capa,202IWL_UCODE_TLV_CAPA_OFFLOAD_BTM_SUPPORT))203enabled |= IWL_D3_PROTO_OFFLOAD_BTM;204205if (!disable_offloading)206common->enabled = cpu_to_le32(enabled);207208if (ver >= 4)209cmd.v4.sta_id = cpu_to_le32(sta_id);210211hcmd.len[0] = size;212return iwl_mvm_send_cmd(mvm, &hcmd);213}214215216