162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BSS client mode implementation 462306a36Sopenharmony_ci * Copyright 2003-2008, Jouni Malinen <j@w1.fi> 562306a36Sopenharmony_ci * Copyright 2004, Instant802 Networks, Inc. 662306a36Sopenharmony_ci * Copyright 2005, Devicescape Software, Inc. 762306a36Sopenharmony_ci * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 862306a36Sopenharmony_ci * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 962306a36Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 1062306a36Sopenharmony_ci * Copyright (C) 2015 - 2017 Intel Deutschland GmbH 1162306a36Sopenharmony_ci * Copyright (C) 2018 - 2023 Intel Corporation 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/fips.h> 1662306a36Sopenharmony_ci#include <linux/if_ether.h> 1762306a36Sopenharmony_ci#include <linux/skbuff.h> 1862306a36Sopenharmony_ci#include <linux/if_arp.h> 1962306a36Sopenharmony_ci#include <linux/etherdevice.h> 2062306a36Sopenharmony_ci#include <linux/moduleparam.h> 2162306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2262306a36Sopenharmony_ci#include <linux/crc32.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/export.h> 2562306a36Sopenharmony_ci#include <net/mac80211.h> 2662306a36Sopenharmony_ci#include <asm/unaligned.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "ieee80211_i.h" 2962306a36Sopenharmony_ci#include "driver-ops.h" 3062306a36Sopenharmony_ci#include "rate.h" 3162306a36Sopenharmony_ci#include "led.h" 3262306a36Sopenharmony_ci#include "fils_aead.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define IEEE80211_AUTH_TIMEOUT (HZ / 5) 3562306a36Sopenharmony_ci#define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) 3662306a36Sopenharmony_ci#define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10) 3762306a36Sopenharmony_ci#define IEEE80211_AUTH_TIMEOUT_SAE (HZ * 2) 3862306a36Sopenharmony_ci#define IEEE80211_AUTH_MAX_TRIES 3 3962306a36Sopenharmony_ci#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) 4062306a36Sopenharmony_ci#define IEEE80211_AUTH_WAIT_SAE_RETRY (HZ * 2) 4162306a36Sopenharmony_ci#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) 4262306a36Sopenharmony_ci#define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2) 4362306a36Sopenharmony_ci#define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10) 4462306a36Sopenharmony_ci#define IEEE80211_ASSOC_MAX_TRIES 3 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int max_nullfunc_tries = 2; 4762306a36Sopenharmony_cimodule_param(max_nullfunc_tries, int, 0644); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(max_nullfunc_tries, 4962306a36Sopenharmony_ci "Maximum nullfunc tx tries before disconnecting (reason 4)."); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int max_probe_tries = 5; 5262306a36Sopenharmony_cimodule_param(max_probe_tries, int, 0644); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(max_probe_tries, 5462306a36Sopenharmony_ci "Maximum probe tries before disconnecting (reason 4)."); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Beacon loss timeout is calculated as N frames times the 5862306a36Sopenharmony_ci * advertised beacon interval. This may need to be somewhat 5962306a36Sopenharmony_ci * higher than what hardware might detect to account for 6062306a36Sopenharmony_ci * delays in the host processing frames. But since we also 6162306a36Sopenharmony_ci * probe on beacon miss before declaring the connection lost 6262306a36Sopenharmony_ci * default to what we want. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic int beacon_loss_count = 7; 6562306a36Sopenharmony_cimodule_param(beacon_loss_count, int, 0644); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(beacon_loss_count, 6762306a36Sopenharmony_ci "Number of beacon intervals before we decide beacon was lost."); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Time the connection can be idle before we probe 7162306a36Sopenharmony_ci * it to see if we can still talk to the AP. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci#define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ) 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Time we wait for a probe response after sending 7662306a36Sopenharmony_ci * a probe request because of beacon loss or for 7762306a36Sopenharmony_ci * checking the connection still works. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic int probe_wait_ms = 500; 8062306a36Sopenharmony_cimodule_param(probe_wait_ms, int, 0644); 8162306a36Sopenharmony_ciMODULE_PARM_DESC(probe_wait_ms, 8262306a36Sopenharmony_ci "Maximum time(ms) to wait for probe response" 8362306a36Sopenharmony_ci " before disconnecting (reason 4)."); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * How many Beacon frames need to have been used in average signal strength 8762306a36Sopenharmony_ci * before starting to indicate signal change events. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci#define IEEE80211_SIGNAL_AVE_MIN_COUNT 4 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * Extract from the given disabled subchannel bitmap (raw format 9362306a36Sopenharmony_ci * from the EHT Operation Element) the bits for the subchannel 9462306a36Sopenharmony_ci * we're using right now. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic u16 9762306a36Sopenharmony_ciieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper, 9862306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, u16 bitmap) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional; 10162306a36Sopenharmony_ci struct cfg80211_chan_def ap_chandef = *chandef; 10262306a36Sopenharmony_ci u32 ap_center_freq, local_center_freq; 10362306a36Sopenharmony_ci u32 ap_bw, local_bw; 10462306a36Sopenharmony_ci int ap_start_freq, local_start_freq; 10562306a36Sopenharmony_ci u16 shift, mask; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) || 10862306a36Sopenharmony_ci !(eht_oper->params & 10962306a36Sopenharmony_ci IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* set 160/320 supported to get the full AP definition */ 11362306a36Sopenharmony_ci ieee80211_chandef_eht_oper(eht_oper, true, true, &ap_chandef); 11462306a36Sopenharmony_ci ap_center_freq = ap_chandef.center_freq1; 11562306a36Sopenharmony_ci ap_bw = 20 * BIT(u8_get_bits(info->control, 11662306a36Sopenharmony_ci IEEE80211_EHT_OPER_CHAN_WIDTH)); 11762306a36Sopenharmony_ci ap_start_freq = ap_center_freq - ap_bw / 2; 11862306a36Sopenharmony_ci local_center_freq = chandef->center_freq1; 11962306a36Sopenharmony_ci local_bw = 20 * BIT(ieee80211_chan_width_to_rx_bw(chandef->width)); 12062306a36Sopenharmony_ci local_start_freq = local_center_freq - local_bw / 2; 12162306a36Sopenharmony_ci shift = (local_start_freq - ap_start_freq) / 20; 12262306a36Sopenharmony_ci mask = BIT(local_bw / 20) - 1; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return (bitmap >> shift) & mask; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * Handle the puncturing bitmap, possibly downgrading bandwidth to get a 12962306a36Sopenharmony_ci * valid bitmap. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic void 13262306a36Sopenharmony_ciieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link, 13362306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper, 13462306a36Sopenharmony_ci u16 bitmap, u64 *changed) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct cfg80211_chan_def *chandef = &link->conf->chandef; 13762306a36Sopenharmony_ci u16 extracted; 13862306a36Sopenharmony_ci u64 _changed = 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!changed) 14162306a36Sopenharmony_ci changed = &_changed; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci while (chandef->width > NL80211_CHAN_WIDTH_40) { 14462306a36Sopenharmony_ci extracted = 14562306a36Sopenharmony_ci ieee80211_extract_dis_subch_bmap(eht_oper, chandef, 14662306a36Sopenharmony_ci bitmap); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (cfg80211_valid_disable_subchannel_bitmap(&bitmap, 14962306a36Sopenharmony_ci chandef)) 15062306a36Sopenharmony_ci break; 15162306a36Sopenharmony_ci link->u.mgd.conn_flags |= 15262306a36Sopenharmony_ci ieee80211_chandef_downgrade(chandef); 15362306a36Sopenharmony_ci *changed |= BSS_CHANGED_BANDWIDTH; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (chandef->width <= NL80211_CHAN_WIDTH_40) 15762306a36Sopenharmony_ci extracted = 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (link->conf->eht_puncturing != extracted) { 16062306a36Sopenharmony_ci link->conf->eht_puncturing = extracted; 16162306a36Sopenharmony_ci *changed |= BSS_CHANGED_EHT_PUNCTURING; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * We can have multiple work items (and connection probing) 16762306a36Sopenharmony_ci * scheduling this timer, but we need to take care to only 16862306a36Sopenharmony_ci * reschedule it when it should fire _earlier_ than it was 16962306a36Sopenharmony_ci * asked for before, or if it's not pending right now. This 17062306a36Sopenharmony_ci * function ensures that. Note that it then is required to 17162306a36Sopenharmony_ci * run this function for all timeouts after the first one 17262306a36Sopenharmony_ci * has happened -- the work that runs from this timer will 17362306a36Sopenharmony_ci * do that. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistatic void run_again(struct ieee80211_sub_if_data *sdata, 17662306a36Sopenharmony_ci unsigned long timeout) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci sdata_assert_lock(sdata); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!timer_pending(&sdata->u.mgd.timer) || 18162306a36Sopenharmony_ci time_before(timeout, sdata->u.mgd.timer.expires)) 18262306a36Sopenharmony_ci mod_timer(&sdata->u.mgd.timer, timeout); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_civoid ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) 19162306a36Sopenharmony_ci return; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci mod_timer(&sdata->u.mgd.bcn_mon_timer, 19462306a36Sopenharmony_ci round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout)); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_civoid ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (unlikely(!ifmgd->associated)) 20262306a36Sopenharmony_ci return; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (ifmgd->probe_send_count) 20562306a36Sopenharmony_ci ifmgd->probe_send_count = 0; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci mod_timer(&ifmgd->conn_mon_timer, 21162306a36Sopenharmony_ci round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int ecw2cw(int ecw) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci return (1 << ecw) - 1; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic ieee80211_conn_flags_t 22062306a36Sopenharmony_ciieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, 22162306a36Sopenharmony_ci struct ieee80211_link_data *link, 22262306a36Sopenharmony_ci ieee80211_conn_flags_t conn_flags, 22362306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 22462306a36Sopenharmony_ci struct ieee80211_channel *channel, 22562306a36Sopenharmony_ci u32 vht_cap_info, 22662306a36Sopenharmony_ci const struct ieee80211_ht_operation *ht_oper, 22762306a36Sopenharmony_ci const struct ieee80211_vht_operation *vht_oper, 22862306a36Sopenharmony_ci const struct ieee80211_he_operation *he_oper, 22962306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper, 23062306a36Sopenharmony_ci const struct ieee80211_s1g_oper_ie *s1g_oper, 23162306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, bool tracking) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct cfg80211_chan_def vht_chandef; 23462306a36Sopenharmony_ci struct ieee80211_sta_ht_cap sta_ht_cap; 23562306a36Sopenharmony_ci ieee80211_conn_flags_t ret; 23662306a36Sopenharmony_ci u32 ht_cfreq; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci memset(chandef, 0, sizeof(struct cfg80211_chan_def)); 23962306a36Sopenharmony_ci chandef->chan = channel; 24062306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_20_NOHT; 24162306a36Sopenharmony_ci chandef->center_freq1 = channel->center_freq; 24262306a36Sopenharmony_ci chandef->freq1_offset = channel->freq_offset; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (channel->band == NL80211_BAND_6GHZ) { 24562306a36Sopenharmony_ci if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper, 24662306a36Sopenharmony_ci chandef)) { 24762306a36Sopenharmony_ci mlme_dbg(sdata, 24862306a36Sopenharmony_ci "bad 6 GHz operation, disabling HT/VHT/HE/EHT\n"); 24962306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | 25062306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT | 25162306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 25262306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci ret = 0; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci vht_chandef = *chandef; 25762306a36Sopenharmony_ci goto out; 25862306a36Sopenharmony_ci } else if (sband->band == NL80211_BAND_S1GHZ) { 25962306a36Sopenharmony_ci if (!ieee80211_chandef_s1g_oper(s1g_oper, chandef)) { 26062306a36Sopenharmony_ci sdata_info(sdata, 26162306a36Sopenharmony_ci "Missing S1G Operation Element? Trying operating == primary\n"); 26262306a36Sopenharmony_ci chandef->width = ieee80211_s1g_channel_width(channel); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_40MHZ | 26662306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT | 26762306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_80P80MHZ | 26862306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_160MHZ; 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); 27362306a36Sopenharmony_ci ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!ht_oper || !sta_ht_cap.ht_supported) { 27662306a36Sopenharmony_ci mlme_dbg(sdata, "HT operation missing / HT not supported\n"); 27762306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | 27862306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT | 27962306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 28062306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 28162306a36Sopenharmony_ci goto out; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_20; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan, 28762306a36Sopenharmony_ci channel->band); 28862306a36Sopenharmony_ci /* check that channel matches the right operating channel */ 28962306a36Sopenharmony_ci if (!tracking && channel->center_freq != ht_cfreq) { 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * It's possible that some APs are confused here; 29262306a36Sopenharmony_ci * Netgear WNDR3700 sometimes reports 4 higher than 29362306a36Sopenharmony_ci * the actual channel in association responses, but 29462306a36Sopenharmony_ci * since we look at probe response/beacon data here 29562306a36Sopenharmony_ci * it should be OK. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci sdata_info(sdata, 29862306a36Sopenharmony_ci "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n", 29962306a36Sopenharmony_ci channel->center_freq, ht_cfreq, 30062306a36Sopenharmony_ci ht_oper->primary_chan, channel->band); 30162306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | 30262306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT | 30362306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 30462306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* check 40 MHz support, if we have it */ 30962306a36Sopenharmony_ci if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { 31062306a36Sopenharmony_ci ieee80211_chandef_ht_oper(ht_oper, chandef); 31162306a36Sopenharmony_ci } else { 31262306a36Sopenharmony_ci mlme_dbg(sdata, "40 MHz not supported\n"); 31362306a36Sopenharmony_ci /* 40 MHz (and 80 MHz) must be supported for VHT */ 31462306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_VHT; 31562306a36Sopenharmony_ci /* also mark 40 MHz disabled */ 31662306a36Sopenharmony_ci ret |= IEEE80211_CONN_DISABLE_40MHZ; 31762306a36Sopenharmony_ci goto out; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!vht_oper || !sband->vht_cap.vht_supported) { 32162306a36Sopenharmony_ci mlme_dbg(sdata, "VHT operation missing / VHT not supported\n"); 32262306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_VHT; 32362306a36Sopenharmony_ci goto out; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci vht_chandef = *chandef; 32762306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_HE) && 32862306a36Sopenharmony_ci he_oper && 32962306a36Sopenharmony_ci (le32_to_cpu(he_oper->he_oper_params) & 33062306a36Sopenharmony_ci IEEE80211_HE_OPERATION_VHT_OPER_INFO)) { 33162306a36Sopenharmony_ci struct ieee80211_vht_operation he_oper_vht_cap; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * Set only first 3 bytes (other 2 aren't used in 33562306a36Sopenharmony_ci * ieee80211_chandef_vht_oper() anyway) 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci memcpy(&he_oper_vht_cap, he_oper->optional, 3); 33862306a36Sopenharmony_ci he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info, 34162306a36Sopenharmony_ci &he_oper_vht_cap, ht_oper, 34262306a36Sopenharmony_ci &vht_chandef)) { 34362306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_HE)) 34462306a36Sopenharmony_ci sdata_info(sdata, 34562306a36Sopenharmony_ci "HE AP VHT information is invalid, disabling HE\n"); 34662306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; 34762306a36Sopenharmony_ci goto out; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } else if (!ieee80211_chandef_vht_oper(&sdata->local->hw, 35062306a36Sopenharmony_ci vht_cap_info, 35162306a36Sopenharmony_ci vht_oper, ht_oper, 35262306a36Sopenharmony_ci &vht_chandef)) { 35362306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) 35462306a36Sopenharmony_ci sdata_info(sdata, 35562306a36Sopenharmony_ci "AP VHT information is invalid, disabling VHT\n"); 35662306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_VHT; 35762306a36Sopenharmony_ci goto out; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!cfg80211_chandef_valid(&vht_chandef)) { 36162306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) 36262306a36Sopenharmony_ci sdata_info(sdata, 36362306a36Sopenharmony_ci "AP VHT information is invalid, disabling VHT\n"); 36462306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_VHT; 36562306a36Sopenharmony_ci goto out; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (cfg80211_chandef_identical(chandef, &vht_chandef)) { 36962306a36Sopenharmony_ci ret = 0; 37062306a36Sopenharmony_ci goto out; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) { 37462306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT)) 37562306a36Sopenharmony_ci sdata_info(sdata, 37662306a36Sopenharmony_ci "AP VHT information doesn't match HT, disabling VHT\n"); 37762306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_VHT; 37862306a36Sopenharmony_ci goto out; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci *chandef = vht_chandef; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * handle the case that the EHT operation indicates that it holds EHT 38562306a36Sopenharmony_ci * operation information (in case that the channel width differs from 38662306a36Sopenharmony_ci * the channel width reported in HT/VHT/HE). 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { 38962306a36Sopenharmony_ci struct cfg80211_chan_def eht_chandef = *chandef; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ieee80211_chandef_eht_oper(eht_oper, 39262306a36Sopenharmony_ci eht_chandef.width == 39362306a36Sopenharmony_ci NL80211_CHAN_WIDTH_160, 39462306a36Sopenharmony_ci false, &eht_chandef); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (!cfg80211_chandef_valid(&eht_chandef)) { 39762306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT)) 39862306a36Sopenharmony_ci sdata_info(sdata, 39962306a36Sopenharmony_ci "AP EHT information is invalid, disabling EHT\n"); 40062306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_EHT; 40162306a36Sopenharmony_ci goto out; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (!cfg80211_chandef_compatible(chandef, &eht_chandef)) { 40562306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT)) 40662306a36Sopenharmony_ci sdata_info(sdata, 40762306a36Sopenharmony_ci "AP EHT information is incompatible, disabling EHT\n"); 40862306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_EHT; 40962306a36Sopenharmony_ci goto out; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci *chandef = eht_chandef; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ret = 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ciout: 41862306a36Sopenharmony_ci /* 41962306a36Sopenharmony_ci * When tracking the current AP, don't do any further checks if the 42062306a36Sopenharmony_ci * new chandef is identical to the one we're currently using for the 42162306a36Sopenharmony_ci * connection. This keeps us from playing ping-pong with regulatory, 42262306a36Sopenharmony_ci * without it the following can happen (for example): 42362306a36Sopenharmony_ci * - connect to an AP with 80 MHz, world regdom allows 80 MHz 42462306a36Sopenharmony_ci * - AP advertises regdom US 42562306a36Sopenharmony_ci * - CRDA loads regdom US with 80 MHz prohibited (old database) 42662306a36Sopenharmony_ci * - the code below detects an unsupported channel, downgrades, and 42762306a36Sopenharmony_ci * we disconnect from the AP in the caller 42862306a36Sopenharmony_ci * - disconnect causes CRDA to reload world regdomain and the game 42962306a36Sopenharmony_ci * starts anew. 43062306a36Sopenharmony_ci * (see https://bugzilla.kernel.org/show_bug.cgi?id=70881) 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * It seems possible that there are still scenarios with CSA or real 43362306a36Sopenharmony_ci * bandwidth changes where a this could happen, but those cases are 43462306a36Sopenharmony_ci * less common and wouldn't completely prevent using the AP. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (tracking && 43762306a36Sopenharmony_ci cfg80211_chandef_identical(chandef, &link->conf->chandef)) 43862306a36Sopenharmony_ci return ret; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* don't print the message below for VHT mismatch if VHT is disabled */ 44162306a36Sopenharmony_ci if (ret & IEEE80211_CONN_DISABLE_VHT) 44262306a36Sopenharmony_ci vht_chandef = *chandef; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci /* 44562306a36Sopenharmony_ci * Ignore the DISABLED flag when we're already connected and only 44662306a36Sopenharmony_ci * tracking the APs beacon for bandwidth changes - otherwise we 44762306a36Sopenharmony_ci * might get disconnected here if we connect to an AP, update our 44862306a36Sopenharmony_ci * regulatory information based on the AP's country IE and the 44962306a36Sopenharmony_ci * information we have is wrong/outdated and disables the channel 45062306a36Sopenharmony_ci * that we're actually using for the connection to the AP. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, 45362306a36Sopenharmony_ci tracking ? 0 : 45462306a36Sopenharmony_ci IEEE80211_CHAN_DISABLED)) { 45562306a36Sopenharmony_ci if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) { 45662306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | 45762306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT | 45862306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 45962306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ret |= ieee80211_chandef_downgrade(chandef); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!he_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, 46762306a36Sopenharmony_ci IEEE80211_CHAN_NO_HE)) 46862306a36Sopenharmony_ci ret |= IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!eht_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef, 47162306a36Sopenharmony_ci IEEE80211_CHAN_NO_EHT)) 47262306a36Sopenharmony_ci ret |= IEEE80211_CONN_DISABLE_EHT; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (chandef->width != vht_chandef.width && !tracking) 47562306a36Sopenharmony_ci sdata_info(sdata, 47662306a36Sopenharmony_ci "capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n"); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci WARN_ON_ONCE(!cfg80211_chandef_valid(chandef)); 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int ieee80211_config_bw(struct ieee80211_link_data *link, 48362306a36Sopenharmony_ci const struct ieee80211_ht_cap *ht_cap, 48462306a36Sopenharmony_ci const struct ieee80211_vht_cap *vht_cap, 48562306a36Sopenharmony_ci const struct ieee80211_ht_operation *ht_oper, 48662306a36Sopenharmony_ci const struct ieee80211_vht_operation *vht_oper, 48762306a36Sopenharmony_ci const struct ieee80211_he_operation *he_oper, 48862306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper, 48962306a36Sopenharmony_ci const struct ieee80211_s1g_oper_ie *s1g_oper, 49062306a36Sopenharmony_ci const u8 *bssid, u64 *changed) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 49362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 49462306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 49562306a36Sopenharmony_ci struct ieee80211_channel *chan = link->conf->chandef.chan; 49662306a36Sopenharmony_ci struct ieee80211_supported_band *sband = 49762306a36Sopenharmony_ci local->hw.wiphy->bands[chan->band]; 49862306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 49962306a36Sopenharmony_ci u16 ht_opmode; 50062306a36Sopenharmony_ci ieee80211_conn_flags_t flags; 50162306a36Sopenharmony_ci u32 vht_cap_info = 0; 50262306a36Sopenharmony_ci int ret; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* if HT was/is disabled, don't track any bandwidth changes */ 50562306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT || !ht_oper) 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* don't check VHT if we associated as non-VHT station */ 50962306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) 51062306a36Sopenharmony_ci vht_oper = NULL; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* don't check HE if we associated as non-HE station */ 51362306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE || 51462306a36Sopenharmony_ci !ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif)) { 51562306a36Sopenharmony_ci he_oper = NULL; 51662306a36Sopenharmony_ci eht_oper = NULL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* don't check EHT if we associated as non-EHT station */ 52062306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT || 52162306a36Sopenharmony_ci !ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif)) 52262306a36Sopenharmony_ci eht_oper = NULL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * if bss configuration changed store the new one - 52662306a36Sopenharmony_ci * this may be applicable even if channel is identical 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci ht_opmode = le16_to_cpu(ht_oper->operation_mode); 52962306a36Sopenharmony_ci if (link->conf->ht_operation_mode != ht_opmode) { 53062306a36Sopenharmony_ci *changed |= BSS_CHANGED_HT; 53162306a36Sopenharmony_ci link->conf->ht_operation_mode = ht_opmode; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (vht_cap) 53562306a36Sopenharmony_ci vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* calculate new channel (type) based on HT/VHT/HE operation IEs */ 53862306a36Sopenharmony_ci flags = ieee80211_determine_chantype(sdata, link, 53962306a36Sopenharmony_ci link->u.mgd.conn_flags, 54062306a36Sopenharmony_ci sband, chan, vht_cap_info, 54162306a36Sopenharmony_ci ht_oper, vht_oper, 54262306a36Sopenharmony_ci he_oper, eht_oper, 54362306a36Sopenharmony_ci s1g_oper, &chandef, true); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * Downgrade the new channel if we associated with restricted 54762306a36Sopenharmony_ci * capabilities. For example, if we associated as a 20 MHz STA 54862306a36Sopenharmony_ci * to a 40 MHz AP (due to regulatory, capabilities or config 54962306a36Sopenharmony_ci * reasons) then switching to a 40 MHz channel now won't do us 55062306a36Sopenharmony_ci * any good -- we couldn't use it with the AP. 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ && 55362306a36Sopenharmony_ci chandef.width == NL80211_CHAN_WIDTH_80P80) 55462306a36Sopenharmony_ci flags |= ieee80211_chandef_downgrade(&chandef); 55562306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_160MHZ && 55662306a36Sopenharmony_ci chandef.width == NL80211_CHAN_WIDTH_160) 55762306a36Sopenharmony_ci flags |= ieee80211_chandef_downgrade(&chandef); 55862306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_40MHZ && 55962306a36Sopenharmony_ci chandef.width > NL80211_CHAN_WIDTH_20) 56062306a36Sopenharmony_ci flags |= ieee80211_chandef_downgrade(&chandef); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (cfg80211_chandef_identical(&chandef, &link->conf->chandef)) 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci link_info(link, 56662306a36Sopenharmony_ci "AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n", 56762306a36Sopenharmony_ci link->u.mgd.bssid, chandef.chan->center_freq, 56862306a36Sopenharmony_ci chandef.chan->freq_offset, chandef.width, 56962306a36Sopenharmony_ci chandef.center_freq1, chandef.freq1_offset, 57062306a36Sopenharmony_ci chandef.center_freq2); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (flags != (link->u.mgd.conn_flags & 57362306a36Sopenharmony_ci (IEEE80211_CONN_DISABLE_HT | 57462306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT | 57562306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 57662306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT | 57762306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_40MHZ | 57862306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_80P80MHZ | 57962306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_160MHZ | 58062306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_320MHZ)) || 58162306a36Sopenharmony_ci !cfg80211_chandef_valid(&chandef)) { 58262306a36Sopenharmony_ci sdata_info(sdata, 58362306a36Sopenharmony_ci "AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n", 58462306a36Sopenharmony_ci link->u.mgd.bssid, flags, ifmgd->flags); 58562306a36Sopenharmony_ci return -EINVAL; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = ieee80211_link_change_bandwidth(link, &chandef, changed); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (ret) { 59162306a36Sopenharmony_ci sdata_info(sdata, 59262306a36Sopenharmony_ci "AP %pM changed bandwidth to incompatible one - disconnect\n", 59362306a36Sopenharmony_ci link->u.mgd.bssid); 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* frame sending functions */ 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata, 60362306a36Sopenharmony_ci struct sk_buff *skb, u8 ap_ht_param, 60462306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 60562306a36Sopenharmony_ci struct ieee80211_channel *channel, 60662306a36Sopenharmony_ci enum ieee80211_smps_mode smps, 60762306a36Sopenharmony_ci ieee80211_conn_flags_t conn_flags) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci u8 *pos; 61062306a36Sopenharmony_ci u32 flags = channel->flags; 61162306a36Sopenharmony_ci u16 cap; 61262306a36Sopenharmony_ci struct ieee80211_sta_ht_cap ht_cap; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap)); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); 61762306a36Sopenharmony_ci ieee80211_apply_htcap_overrides(sdata, &ht_cap); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* determine capability flags */ 62062306a36Sopenharmony_ci cap = ht_cap.cap; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 62362306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 62462306a36Sopenharmony_ci if (flags & IEEE80211_CHAN_NO_HT40PLUS) { 62562306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 62662306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SGI_40; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 63062306a36Sopenharmony_ci if (flags & IEEE80211_CHAN_NO_HT40MINUS) { 63162306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 63262306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SGI_40; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci break; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * If 40 MHz was disabled associate as though we weren't 63962306a36Sopenharmony_ci * capable of 40 MHz -- some broken APs will never fall 64062306a36Sopenharmony_ci * back to trying to transmit in 20 MHz. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_ci if (conn_flags & IEEE80211_CONN_DISABLE_40MHZ) { 64362306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 64462306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SGI_40; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* set SM PS mode properly */ 64862306a36Sopenharmony_ci cap &= ~IEEE80211_HT_CAP_SM_PS; 64962306a36Sopenharmony_ci switch (smps) { 65062306a36Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 65162306a36Sopenharmony_ci case IEEE80211_SMPS_NUM_MODES: 65262306a36Sopenharmony_ci WARN_ON(1); 65362306a36Sopenharmony_ci fallthrough; 65462306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 65562306a36Sopenharmony_ci cap |= WLAN_HT_CAP_SM_PS_DISABLED << 65662306a36Sopenharmony_ci IEEE80211_HT_CAP_SM_PS_SHIFT; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 65962306a36Sopenharmony_ci cap |= WLAN_HT_CAP_SM_PS_STATIC << 66062306a36Sopenharmony_ci IEEE80211_HT_CAP_SM_PS_SHIFT; 66162306a36Sopenharmony_ci break; 66262306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 66362306a36Sopenharmony_ci cap |= WLAN_HT_CAP_SM_PS_DYNAMIC << 66462306a36Sopenharmony_ci IEEE80211_HT_CAP_SM_PS_SHIFT; 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* reserve and fill IE */ 66962306a36Sopenharmony_ci pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); 67062306a36Sopenharmony_ci ieee80211_ie_build_ht_cap(pos, &ht_cap, cap); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci/* This function determines vht capability flags for the association 67462306a36Sopenharmony_ci * and builds the IE. 67562306a36Sopenharmony_ci * Note - the function returns true to own the MU-MIMO capability 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_cistatic bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata, 67862306a36Sopenharmony_ci struct sk_buff *skb, 67962306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 68062306a36Sopenharmony_ci struct ieee80211_vht_cap *ap_vht_cap, 68162306a36Sopenharmony_ci ieee80211_conn_flags_t conn_flags) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 68462306a36Sopenharmony_ci u8 *pos; 68562306a36Sopenharmony_ci u32 cap; 68662306a36Sopenharmony_ci struct ieee80211_sta_vht_cap vht_cap; 68762306a36Sopenharmony_ci u32 mask, ap_bf_sts, our_bf_sts; 68862306a36Sopenharmony_ci bool mu_mimo_owner = false; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap)); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); 69362306a36Sopenharmony_ci ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* determine capability flags */ 69662306a36Sopenharmony_ci cap = vht_cap.cap; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ) { 69962306a36Sopenharmony_ci u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 70262306a36Sopenharmony_ci if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || 70362306a36Sopenharmony_ci bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 70462306a36Sopenharmony_ci cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ) { 70862306a36Sopenharmony_ci cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160; 70962306a36Sopenharmony_ci cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * Some APs apparently get confused if our capabilities are better 71462306a36Sopenharmony_ci * than theirs, so restrict what we advertise in the assoc request. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci if (!(ap_vht_cap->vht_cap_info & 71762306a36Sopenharmony_ci cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE))) 71862306a36Sopenharmony_ci cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | 71962306a36Sopenharmony_ci IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); 72062306a36Sopenharmony_ci else if (!(ap_vht_cap->vht_cap_info & 72162306a36Sopenharmony_ci cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE))) 72262306a36Sopenharmony_ci cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* 72562306a36Sopenharmony_ci * If some other vif is using the MU-MIMO capability we cannot associate 72662306a36Sopenharmony_ci * using MU-MIMO - this will lead to contradictions in the group-id 72762306a36Sopenharmony_ci * mechanism. 72862306a36Sopenharmony_ci * Ownership is defined since association request, in order to avoid 72962306a36Sopenharmony_ci * simultaneous associations with MU-MIMO. 73062306a36Sopenharmony_ci */ 73162306a36Sopenharmony_ci if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) { 73262306a36Sopenharmony_ci bool disable_mu_mimo = false; 73362306a36Sopenharmony_ci struct ieee80211_sub_if_data *other; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci list_for_each_entry_rcu(other, &local->interfaces, list) { 73662306a36Sopenharmony_ci if (other->vif.bss_conf.mu_mimo_owner) { 73762306a36Sopenharmony_ci disable_mu_mimo = true; 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci if (disable_mu_mimo) 74262306a36Sopenharmony_ci cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 74362306a36Sopenharmony_ci else 74462306a36Sopenharmony_ci mu_mimo_owner = true; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask; 75062306a36Sopenharmony_ci our_bf_sts = cap & mask; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (ap_bf_sts < our_bf_sts) { 75362306a36Sopenharmony_ci cap &= ~mask; 75462306a36Sopenharmony_ci cap |= ap_bf_sts; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci /* reserve and fill IE */ 75862306a36Sopenharmony_ci pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); 75962306a36Sopenharmony_ci ieee80211_ie_build_vht_cap(pos, &vht_cap, cap); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return mu_mimo_owner; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci/* This function determines HE capability flags for the association 76562306a36Sopenharmony_ci * and builds the IE. 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_cistatic void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata, 76862306a36Sopenharmony_ci struct sk_buff *skb, 76962306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 77062306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode, 77162306a36Sopenharmony_ci ieee80211_conn_flags_t conn_flags) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci u8 *pos, *pre_he_pos; 77462306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 77562306a36Sopenharmony_ci u8 he_cap_size; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 77862306a36Sopenharmony_ci if (WARN_ON(!he_cap)) 77962306a36Sopenharmony_ci return; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* get a max size estimate */ 78262306a36Sopenharmony_ci he_cap_size = 78362306a36Sopenharmony_ci 2 + 1 + sizeof(he_cap->he_cap_elem) + 78462306a36Sopenharmony_ci ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) + 78562306a36Sopenharmony_ci ieee80211_he_ppe_size(he_cap->ppe_thres[0], 78662306a36Sopenharmony_ci he_cap->he_cap_elem.phy_cap_info); 78762306a36Sopenharmony_ci pos = skb_put(skb, he_cap_size); 78862306a36Sopenharmony_ci pre_he_pos = pos; 78962306a36Sopenharmony_ci pos = ieee80211_ie_build_he_cap(conn_flags, 79062306a36Sopenharmony_ci pos, he_cap, pos + he_cap_size); 79162306a36Sopenharmony_ci /* trim excess if any */ 79262306a36Sopenharmony_ci skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos)); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ieee80211_ie_build_he_6ghz_cap(sdata, smps_mode, skb); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata, 79862306a36Sopenharmony_ci struct sk_buff *skb, 79962306a36Sopenharmony_ci struct ieee80211_supported_band *sband) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci u8 *pos; 80262306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 80362306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap; 80462306a36Sopenharmony_ci u8 eht_cap_size; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 80762306a36Sopenharmony_ci eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* 81062306a36Sopenharmony_ci * EHT capabilities element is only added if the HE capabilities element 81162306a36Sopenharmony_ci * was added so assume that 'he_cap' is valid and don't check it. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci if (WARN_ON(!he_cap || !eht_cap)) 81462306a36Sopenharmony_ci return; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci eht_cap_size = 81762306a36Sopenharmony_ci 2 + 1 + sizeof(eht_cap->eht_cap_elem) + 81862306a36Sopenharmony_ci ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, 81962306a36Sopenharmony_ci &eht_cap->eht_cap_elem, 82062306a36Sopenharmony_ci false) + 82162306a36Sopenharmony_ci ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], 82262306a36Sopenharmony_ci eht_cap->eht_cap_elem.phy_cap_info); 82362306a36Sopenharmony_ci pos = skb_put(skb, eht_cap_size); 82462306a36Sopenharmony_ci ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size, 82562306a36Sopenharmony_ci false); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void ieee80211_assoc_add_rates(struct sk_buff *skb, 82962306a36Sopenharmony_ci enum nl80211_chan_width width, 83062306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 83162306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci unsigned int shift = ieee80211_chanwidth_get_shift(width); 83462306a36Sopenharmony_ci unsigned int rates_len, supp_rates_len; 83562306a36Sopenharmony_ci u32 rates = 0; 83662306a36Sopenharmony_ci int i, count; 83762306a36Sopenharmony_ci u8 *pos; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (assoc_data->supp_rates_len) { 84062306a36Sopenharmony_ci /* 84162306a36Sopenharmony_ci * Get all rates supported by the device and the AP as 84262306a36Sopenharmony_ci * some APs don't like getting a superset of their rates 84362306a36Sopenharmony_ci * in the association request (e.g. D-Link DAP 1353 in 84462306a36Sopenharmony_ci * b-only mode)... 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci rates_len = ieee80211_parse_bitrates(width, sband, 84762306a36Sopenharmony_ci assoc_data->supp_rates, 84862306a36Sopenharmony_ci assoc_data->supp_rates_len, 84962306a36Sopenharmony_ci &rates); 85062306a36Sopenharmony_ci } else { 85162306a36Sopenharmony_ci /* 85262306a36Sopenharmony_ci * In case AP not provide any supported rates information 85362306a36Sopenharmony_ci * before association, we send information element(s) with 85462306a36Sopenharmony_ci * all rates that we support. 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci rates_len = sband->n_bitrates; 85762306a36Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) 85862306a36Sopenharmony_ci rates |= BIT(i); 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci supp_rates_len = rates_len; 86262306a36Sopenharmony_ci if (supp_rates_len > 8) 86362306a36Sopenharmony_ci supp_rates_len = 8; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci pos = skb_put(skb, supp_rates_len + 2); 86662306a36Sopenharmony_ci *pos++ = WLAN_EID_SUPP_RATES; 86762306a36Sopenharmony_ci *pos++ = supp_rates_len; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci count = 0; 87062306a36Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 87162306a36Sopenharmony_ci if (BIT(i) & rates) { 87262306a36Sopenharmony_ci int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 87362306a36Sopenharmony_ci 5 * (1 << shift)); 87462306a36Sopenharmony_ci *pos++ = (u8)rate; 87562306a36Sopenharmony_ci if (++count == 8) 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (rates_len > count) { 88162306a36Sopenharmony_ci pos = skb_put(skb, rates_len - count + 2); 88262306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_SUPP_RATES; 88362306a36Sopenharmony_ci *pos++ = rates_len - count; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci for (i++; i < sband->n_bitrates; i++) { 88662306a36Sopenharmony_ci if (BIT(i) & rates) { 88762306a36Sopenharmony_ci int rate; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 89062306a36Sopenharmony_ci 5 * (1 << shift)); 89162306a36Sopenharmony_ci *pos++ = (u8)rate; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic size_t ieee80211_add_before_ht_elems(struct sk_buff *skb, 89862306a36Sopenharmony_ci const u8 *elems, 89962306a36Sopenharmony_ci size_t elems_len, 90062306a36Sopenharmony_ci size_t offset) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci size_t noffset; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci static const u8 before_ht[] = { 90562306a36Sopenharmony_ci WLAN_EID_SSID, 90662306a36Sopenharmony_ci WLAN_EID_SUPP_RATES, 90762306a36Sopenharmony_ci WLAN_EID_EXT_SUPP_RATES, 90862306a36Sopenharmony_ci WLAN_EID_PWR_CAPABILITY, 90962306a36Sopenharmony_ci WLAN_EID_SUPPORTED_CHANNELS, 91062306a36Sopenharmony_ci WLAN_EID_RSN, 91162306a36Sopenharmony_ci WLAN_EID_QOS_CAPA, 91262306a36Sopenharmony_ci WLAN_EID_RRM_ENABLED_CAPABILITIES, 91362306a36Sopenharmony_ci WLAN_EID_MOBILITY_DOMAIN, 91462306a36Sopenharmony_ci WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */ 91562306a36Sopenharmony_ci WLAN_EID_RIC_DATA, /* reassoc only */ 91662306a36Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 91762306a36Sopenharmony_ci }; 91862306a36Sopenharmony_ci static const u8 after_ric[] = { 91962306a36Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 92062306a36Sopenharmony_ci WLAN_EID_HT_CAPABILITY, 92162306a36Sopenharmony_ci WLAN_EID_BSS_COEX_2040, 92262306a36Sopenharmony_ci /* luckily this is almost always there */ 92362306a36Sopenharmony_ci WLAN_EID_EXT_CAPABILITY, 92462306a36Sopenharmony_ci WLAN_EID_QOS_TRAFFIC_CAPA, 92562306a36Sopenharmony_ci WLAN_EID_TIM_BCAST_REQ, 92662306a36Sopenharmony_ci WLAN_EID_INTERWORKING, 92762306a36Sopenharmony_ci /* 60 GHz (Multi-band, DMG, MMS) can't happen */ 92862306a36Sopenharmony_ci WLAN_EID_VHT_CAPABILITY, 92962306a36Sopenharmony_ci WLAN_EID_OPMODE_NOTIF, 93062306a36Sopenharmony_ci }; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (!elems_len) 93362306a36Sopenharmony_ci return offset; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci noffset = ieee80211_ie_split_ric(elems, elems_len, 93662306a36Sopenharmony_ci before_ht, 93762306a36Sopenharmony_ci ARRAY_SIZE(before_ht), 93862306a36Sopenharmony_ci after_ric, 93962306a36Sopenharmony_ci ARRAY_SIZE(after_ric), 94062306a36Sopenharmony_ci offset); 94162306a36Sopenharmony_ci skb_put_data(skb, elems + offset, noffset - offset); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return noffset; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic size_t ieee80211_add_before_vht_elems(struct sk_buff *skb, 94762306a36Sopenharmony_ci const u8 *elems, 94862306a36Sopenharmony_ci size_t elems_len, 94962306a36Sopenharmony_ci size_t offset) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci static const u8 before_vht[] = { 95262306a36Sopenharmony_ci /* 95362306a36Sopenharmony_ci * no need to list the ones split off before HT 95462306a36Sopenharmony_ci * or generated here 95562306a36Sopenharmony_ci */ 95662306a36Sopenharmony_ci WLAN_EID_BSS_COEX_2040, 95762306a36Sopenharmony_ci WLAN_EID_EXT_CAPABILITY, 95862306a36Sopenharmony_ci WLAN_EID_QOS_TRAFFIC_CAPA, 95962306a36Sopenharmony_ci WLAN_EID_TIM_BCAST_REQ, 96062306a36Sopenharmony_ci WLAN_EID_INTERWORKING, 96162306a36Sopenharmony_ci /* 60 GHz (Multi-band, DMG, MMS) can't happen */ 96262306a36Sopenharmony_ci }; 96362306a36Sopenharmony_ci size_t noffset; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (!elems_len) 96662306a36Sopenharmony_ci return offset; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* RIC already taken care of in ieee80211_add_before_ht_elems() */ 96962306a36Sopenharmony_ci noffset = ieee80211_ie_split(elems, elems_len, 97062306a36Sopenharmony_ci before_vht, ARRAY_SIZE(before_vht), 97162306a36Sopenharmony_ci offset); 97262306a36Sopenharmony_ci skb_put_data(skb, elems + offset, noffset - offset); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return noffset; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic size_t ieee80211_add_before_he_elems(struct sk_buff *skb, 97862306a36Sopenharmony_ci const u8 *elems, 97962306a36Sopenharmony_ci size_t elems_len, 98062306a36Sopenharmony_ci size_t offset) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci static const u8 before_he[] = { 98362306a36Sopenharmony_ci /* 98462306a36Sopenharmony_ci * no need to list the ones split off before VHT 98562306a36Sopenharmony_ci * or generated here 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci WLAN_EID_OPMODE_NOTIF, 98862306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE, 98962306a36Sopenharmony_ci /* 11ai elements */ 99062306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_SESSION, 99162306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_PUBLIC_KEY, 99262306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_KEY_CONFIRM, 99362306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_HLP_CONTAINER, 99462306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN, 99562306a36Sopenharmony_ci /* TODO: add 11ah/11aj/11ak elements */ 99662306a36Sopenharmony_ci }; 99762306a36Sopenharmony_ci size_t noffset; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (!elems_len) 100062306a36Sopenharmony_ci return offset; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* RIC already taken care of in ieee80211_add_before_ht_elems() */ 100362306a36Sopenharmony_ci noffset = ieee80211_ie_split(elems, elems_len, 100462306a36Sopenharmony_ci before_he, ARRAY_SIZE(before_he), 100562306a36Sopenharmony_ci offset); 100662306a36Sopenharmony_ci skb_put_data(skb, elems + offset, noffset - offset); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return noffset; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci#define PRESENT_ELEMS_MAX 8 101262306a36Sopenharmony_ci#define PRESENT_ELEM_EXT_OFFS 0x100 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata, 101562306a36Sopenharmony_ci struct sk_buff *skb, u16 capab, 101662306a36Sopenharmony_ci const struct element *ext_capa, 101762306a36Sopenharmony_ci const u16 *present_elems); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_cistatic size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata, 102062306a36Sopenharmony_ci struct sk_buff *skb, u16 *capab, 102162306a36Sopenharmony_ci const struct element *ext_capa, 102262306a36Sopenharmony_ci const u8 *extra_elems, 102362306a36Sopenharmony_ci size_t extra_elems_len, 102462306a36Sopenharmony_ci unsigned int link_id, 102562306a36Sopenharmony_ci struct ieee80211_link_data *link, 102662306a36Sopenharmony_ci u16 *present_elems) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 102962306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 103062306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; 103162306a36Sopenharmony_ci struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; 103262306a36Sopenharmony_ci struct ieee80211_channel *chan = cbss->channel; 103362306a36Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 103462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 103562306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 103662306a36Sopenharmony_ci enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20; 103762306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 103862306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode; 103962306a36Sopenharmony_ci u16 orig_capab = *capab; 104062306a36Sopenharmony_ci size_t offset = 0; 104162306a36Sopenharmony_ci int present_elems_len = 0; 104262306a36Sopenharmony_ci u8 *pos; 104362306a36Sopenharmony_ci int i; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci#define ADD_PRESENT_ELEM(id) do { \ 104662306a36Sopenharmony_ci /* need a last for termination - we use 0 == SSID */ \ 104762306a36Sopenharmony_ci if (!WARN_ON(present_elems_len >= PRESENT_ELEMS_MAX - 1)) \ 104862306a36Sopenharmony_ci present_elems[present_elems_len++] = (id); \ 104962306a36Sopenharmony_ci} while (0) 105062306a36Sopenharmony_ci#define ADD_PRESENT_EXT_ELEM(id) ADD_PRESENT_ELEM(PRESENT_ELEM_EXT_OFFS | (id)) 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (link) 105362306a36Sopenharmony_ci smps_mode = link->smps_mode; 105462306a36Sopenharmony_ci else if (sdata->u.mgd.powersave) 105562306a36Sopenharmony_ci smps_mode = IEEE80211_SMPS_DYNAMIC; 105662306a36Sopenharmony_ci else 105762306a36Sopenharmony_ci smps_mode = IEEE80211_SMPS_OFF; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (link) { 106062306a36Sopenharmony_ci /* 106162306a36Sopenharmony_ci * 5/10 MHz scenarios are only viable without MLO, in which 106262306a36Sopenharmony_ci * case this pointer should be used ... All of this is a bit 106362306a36Sopenharmony_ci * unclear though, not sure this even works at all. 106462306a36Sopenharmony_ci */ 106562306a36Sopenharmony_ci rcu_read_lock(); 106662306a36Sopenharmony_ci chanctx_conf = rcu_dereference(link->conf->chanctx_conf); 106762306a36Sopenharmony_ci if (chanctx_conf) 106862306a36Sopenharmony_ci width = chanctx_conf->def.width; 106962306a36Sopenharmony_ci rcu_read_unlock(); 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci sband = local->hw.wiphy->bands[chan->band]; 107362306a36Sopenharmony_ci iftd = ieee80211_get_sband_iftype_data(sband, iftype); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (sband->band == NL80211_BAND_2GHZ) { 107662306a36Sopenharmony_ci *capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; 107762306a36Sopenharmony_ci *capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if ((cbss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) && 108162306a36Sopenharmony_ci ieee80211_hw_check(&local->hw, SPECTRUM_MGMT)) 108262306a36Sopenharmony_ci *capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci if (sband->band != NL80211_BAND_S1GHZ) 108562306a36Sopenharmony_ci ieee80211_assoc_add_rates(skb, width, sband, assoc_data); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT || 108862306a36Sopenharmony_ci *capab & WLAN_CAPABILITY_RADIO_MEASURE) { 108962306a36Sopenharmony_ci struct cfg80211_chan_def chandef = { 109062306a36Sopenharmony_ci .width = width, 109162306a36Sopenharmony_ci .chan = chan, 109262306a36Sopenharmony_ci }; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci pos = skb_put(skb, 4); 109562306a36Sopenharmony_ci *pos++ = WLAN_EID_PWR_CAPABILITY; 109662306a36Sopenharmony_ci *pos++ = 2; 109762306a36Sopenharmony_ci *pos++ = 0; /* min tx power */ 109862306a36Sopenharmony_ci /* max tx power */ 109962306a36Sopenharmony_ci *pos++ = ieee80211_chandef_max_power(&chandef); 110062306a36Sopenharmony_ci ADD_PRESENT_ELEM(WLAN_EID_PWR_CAPABILITY); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* 110462306a36Sopenharmony_ci * Per spec, we shouldn't include the list of channels if we advertise 110562306a36Sopenharmony_ci * support for extended channel switching, but we've always done that; 110662306a36Sopenharmony_ci * (for now?) apply this restriction only on the (new) 6 GHz band. 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT && 110962306a36Sopenharmony_ci (sband->band != NL80211_BAND_6GHZ || 111062306a36Sopenharmony_ci !ext_capa || ext_capa->datalen < 1 || 111162306a36Sopenharmony_ci !(ext_capa->data[0] & WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING))) { 111262306a36Sopenharmony_ci /* TODO: get this in reg domain format */ 111362306a36Sopenharmony_ci pos = skb_put(skb, 2 * sband->n_channels + 2); 111462306a36Sopenharmony_ci *pos++ = WLAN_EID_SUPPORTED_CHANNELS; 111562306a36Sopenharmony_ci *pos++ = 2 * sband->n_channels; 111662306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 111762306a36Sopenharmony_ci int cf = sband->channels[i].center_freq; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(cf); 112062306a36Sopenharmony_ci *pos++ = 1; /* one channel in the subband*/ 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci ADD_PRESENT_ELEM(WLAN_EID_SUPPORTED_CHANNELS); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci /* if present, add any custom IEs that go before HT */ 112662306a36Sopenharmony_ci offset = ieee80211_add_before_ht_elems(skb, extra_elems, 112762306a36Sopenharmony_ci extra_elems_len, 112862306a36Sopenharmony_ci offset); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (sband->band != NL80211_BAND_6GHZ && 113162306a36Sopenharmony_ci !(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT)) { 113262306a36Sopenharmony_ci ieee80211_add_ht_ie(sdata, skb, 113362306a36Sopenharmony_ci assoc_data->link[link_id].ap_ht_param, 113462306a36Sopenharmony_ci sband, chan, smps_mode, 113562306a36Sopenharmony_ci assoc_data->link[link_id].conn_flags); 113662306a36Sopenharmony_ci ADD_PRESENT_ELEM(WLAN_EID_HT_CAPABILITY); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* if present, add any custom IEs that go before VHT */ 114062306a36Sopenharmony_ci offset = ieee80211_add_before_vht_elems(skb, extra_elems, 114162306a36Sopenharmony_ci extra_elems_len, 114262306a36Sopenharmony_ci offset); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (sband->band != NL80211_BAND_6GHZ && 114562306a36Sopenharmony_ci !(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) { 114662306a36Sopenharmony_ci bool mu_mimo_owner = 114762306a36Sopenharmony_ci ieee80211_add_vht_ie(sdata, skb, sband, 114862306a36Sopenharmony_ci &assoc_data->link[link_id].ap_vht_cap, 114962306a36Sopenharmony_ci assoc_data->link[link_id].conn_flags); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci if (link) 115262306a36Sopenharmony_ci link->conf->mu_mimo_owner = mu_mimo_owner; 115362306a36Sopenharmony_ci ADD_PRESENT_ELEM(WLAN_EID_VHT_CAPABILITY); 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* 115762306a36Sopenharmony_ci * If AP doesn't support HT, mark HE and EHT as disabled. 115862306a36Sopenharmony_ci * If on the 5GHz band, make sure it supports VHT. 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ci if (assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT || 116162306a36Sopenharmony_ci (sband->band == NL80211_BAND_5GHZ && 116262306a36Sopenharmony_ci assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) 116362306a36Sopenharmony_ci assoc_data->link[link_id].conn_flags |= 116462306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 116562306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* if present, add any custom IEs that go before HE */ 116862306a36Sopenharmony_ci offset = ieee80211_add_before_he_elems(skb, extra_elems, 116962306a36Sopenharmony_ci extra_elems_len, 117062306a36Sopenharmony_ci offset); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HE)) { 117362306a36Sopenharmony_ci ieee80211_add_he_ie(sdata, skb, sband, smps_mode, 117462306a36Sopenharmony_ci assoc_data->link[link_id].conn_flags); 117562306a36Sopenharmony_ci ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci /* 117962306a36Sopenharmony_ci * careful - need to know about all the present elems before 118062306a36Sopenharmony_ci * calling ieee80211_assoc_add_ml_elem(), so add this one if 118162306a36Sopenharmony_ci * we're going to put it after the ML element 118262306a36Sopenharmony_ci */ 118362306a36Sopenharmony_ci if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT)) 118462306a36Sopenharmony_ci ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY); 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci if (link_id == assoc_data->assoc_link_id) 118762306a36Sopenharmony_ci ieee80211_assoc_add_ml_elem(sdata, skb, orig_capab, ext_capa, 118862306a36Sopenharmony_ci present_elems); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* crash if somebody gets it wrong */ 119162306a36Sopenharmony_ci present_elems = NULL; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT)) 119462306a36Sopenharmony_ci ieee80211_add_eht_ie(sdata, skb, sband); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (sband->band == NL80211_BAND_S1GHZ) { 119762306a36Sopenharmony_ci ieee80211_add_aid_request_ie(sdata, skb); 119862306a36Sopenharmony_ci ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb); 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len) 120262306a36Sopenharmony_ci skb_put_data(skb, iftd->vendor_elems.data, iftd->vendor_elems.len); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (link) 120562306a36Sopenharmony_ci link->u.mgd.conn_flags = assoc_data->link[link_id].conn_flags; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci return offset; 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic void ieee80211_add_non_inheritance_elem(struct sk_buff *skb, 121162306a36Sopenharmony_ci const u16 *outer, 121262306a36Sopenharmony_ci const u16 *inner) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci unsigned int skb_len = skb->len; 121562306a36Sopenharmony_ci bool at_extension = false; 121662306a36Sopenharmony_ci bool added = false; 121762306a36Sopenharmony_ci int i, j; 121862306a36Sopenharmony_ci u8 *len, *list_len = NULL; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci skb_put_u8(skb, WLAN_EID_EXTENSION); 122162306a36Sopenharmony_ci len = skb_put(skb, 1); 122262306a36Sopenharmony_ci skb_put_u8(skb, WLAN_EID_EXT_NON_INHERITANCE); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci for (i = 0; i < PRESENT_ELEMS_MAX && outer[i]; i++) { 122562306a36Sopenharmony_ci u16 elem = outer[i]; 122662306a36Sopenharmony_ci bool have_inner = false; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* should at least be sorted in the sense of normal -> ext */ 122962306a36Sopenharmony_ci WARN_ON(at_extension && elem < PRESENT_ELEM_EXT_OFFS); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* switch to extension list */ 123262306a36Sopenharmony_ci if (!at_extension && elem >= PRESENT_ELEM_EXT_OFFS) { 123362306a36Sopenharmony_ci at_extension = true; 123462306a36Sopenharmony_ci if (!list_len) 123562306a36Sopenharmony_ci skb_put_u8(skb, 0); 123662306a36Sopenharmony_ci list_len = NULL; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci for (j = 0; j < PRESENT_ELEMS_MAX && inner[j]; j++) { 124062306a36Sopenharmony_ci if (elem == inner[j]) { 124162306a36Sopenharmony_ci have_inner = true; 124262306a36Sopenharmony_ci break; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (have_inner) 124762306a36Sopenharmony_ci continue; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (!list_len) { 125062306a36Sopenharmony_ci list_len = skb_put(skb, 1); 125162306a36Sopenharmony_ci *list_len = 0; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci *list_len += 1; 125462306a36Sopenharmony_ci skb_put_u8(skb, (u8)elem); 125562306a36Sopenharmony_ci added = true; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci /* if we added a list but no extension list, make a zero-len one */ 125962306a36Sopenharmony_ci if (added && (!at_extension || !list_len)) 126062306a36Sopenharmony_ci skb_put_u8(skb, 0); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* if nothing added remove extension element completely */ 126362306a36Sopenharmony_ci if (!added) 126462306a36Sopenharmony_ci skb_trim(skb, skb_len); 126562306a36Sopenharmony_ci else 126662306a36Sopenharmony_ci *len = skb->len - skb_len - 2; 126762306a36Sopenharmony_ci} 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_cistatic void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata, 127062306a36Sopenharmony_ci struct sk_buff *skb, u16 capab, 127162306a36Sopenharmony_ci const struct element *ext_capa, 127262306a36Sopenharmony_ci const u16 *outer_present_elems) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 127562306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 127662306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; 127762306a36Sopenharmony_ci struct ieee80211_multi_link_elem *ml_elem; 127862306a36Sopenharmony_ci struct ieee80211_mle_basic_common_info *common; 127962306a36Sopenharmony_ci const struct wiphy_iftype_ext_capab *ift_ext_capa; 128062306a36Sopenharmony_ci __le16 eml_capa = 0, mld_capa_ops = 0; 128162306a36Sopenharmony_ci unsigned int link_id; 128262306a36Sopenharmony_ci u8 *ml_elem_len; 128362306a36Sopenharmony_ci void *capab_pos; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) 128662306a36Sopenharmony_ci return; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, 128962306a36Sopenharmony_ci ieee80211_vif_type_p2p(&sdata->vif)); 129062306a36Sopenharmony_ci if (ift_ext_capa) { 129162306a36Sopenharmony_ci eml_capa = cpu_to_le16(ift_ext_capa->eml_capabilities); 129262306a36Sopenharmony_ci mld_capa_ops = cpu_to_le16(ift_ext_capa->mld_capa_and_ops); 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci skb_put_u8(skb, WLAN_EID_EXTENSION); 129662306a36Sopenharmony_ci ml_elem_len = skb_put(skb, 1); 129762306a36Sopenharmony_ci skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK); 129862306a36Sopenharmony_ci ml_elem = skb_put(skb, sizeof(*ml_elem)); 129962306a36Sopenharmony_ci ml_elem->control = 130062306a36Sopenharmony_ci cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC | 130162306a36Sopenharmony_ci IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP); 130262306a36Sopenharmony_ci common = skb_put(skb, sizeof(*common)); 130362306a36Sopenharmony_ci common->len = sizeof(*common) + 130462306a36Sopenharmony_ci 2; /* MLD capa/ops */ 130562306a36Sopenharmony_ci memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* add EML_CAPA only if needed, see Draft P802.11be_D2.1, 35.3.17 */ 130862306a36Sopenharmony_ci if (eml_capa & 130962306a36Sopenharmony_ci cpu_to_le16((IEEE80211_EML_CAP_EMLSR_SUPP | 131062306a36Sopenharmony_ci IEEE80211_EML_CAP_EMLMR_SUPPORT))) { 131162306a36Sopenharmony_ci common->len += 2; /* EML capabilities */ 131262306a36Sopenharmony_ci ml_elem->control |= 131362306a36Sopenharmony_ci cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA); 131462306a36Sopenharmony_ci skb_put_data(skb, &eml_capa, sizeof(eml_capa)); 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci /* need indication from userspace to support this */ 131762306a36Sopenharmony_ci mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP); 131862306a36Sopenharmony_ci skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops)); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 132162306a36Sopenharmony_ci u16 link_present_elems[PRESENT_ELEMS_MAX] = {}; 132262306a36Sopenharmony_ci const u8 *extra_elems; 132362306a36Sopenharmony_ci size_t extra_elems_len; 132462306a36Sopenharmony_ci size_t extra_used; 132562306a36Sopenharmony_ci u8 *subelem_len = NULL; 132662306a36Sopenharmony_ci __le16 ctrl; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci if (!assoc_data->link[link_id].bss || 132962306a36Sopenharmony_ci link_id == assoc_data->assoc_link_id) 133062306a36Sopenharmony_ci continue; 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci extra_elems = assoc_data->link[link_id].elems; 133362306a36Sopenharmony_ci extra_elems_len = assoc_data->link[link_id].elems_len; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE); 133662306a36Sopenharmony_ci subelem_len = skb_put(skb, 1); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci ctrl = cpu_to_le16(link_id | 133962306a36Sopenharmony_ci IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE | 134062306a36Sopenharmony_ci IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT); 134162306a36Sopenharmony_ci skb_put_data(skb, &ctrl, sizeof(ctrl)); 134262306a36Sopenharmony_ci skb_put_u8(skb, 1 + ETH_ALEN); /* STA Info Length */ 134362306a36Sopenharmony_ci skb_put_data(skb, assoc_data->link[link_id].addr, 134462306a36Sopenharmony_ci ETH_ALEN); 134562306a36Sopenharmony_ci /* 134662306a36Sopenharmony_ci * Now add the contents of the (re)association request, 134762306a36Sopenharmony_ci * but the "listen interval" and "current AP address" 134862306a36Sopenharmony_ci * (if applicable) are skipped. So we only have 134962306a36Sopenharmony_ci * the capability field (remember the position and fill 135062306a36Sopenharmony_ci * later), followed by the elements added below by 135162306a36Sopenharmony_ci * calling ieee80211_assoc_link_elems(). 135262306a36Sopenharmony_ci */ 135362306a36Sopenharmony_ci capab_pos = skb_put(skb, 2); 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci extra_used = ieee80211_assoc_link_elems(sdata, skb, &capab, 135662306a36Sopenharmony_ci ext_capa, 135762306a36Sopenharmony_ci extra_elems, 135862306a36Sopenharmony_ci extra_elems_len, 135962306a36Sopenharmony_ci link_id, NULL, 136062306a36Sopenharmony_ci link_present_elems); 136162306a36Sopenharmony_ci if (extra_elems) 136262306a36Sopenharmony_ci skb_put_data(skb, extra_elems + extra_used, 136362306a36Sopenharmony_ci extra_elems_len - extra_used); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci put_unaligned_le16(capab, capab_pos); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci ieee80211_add_non_inheritance_elem(skb, outer_present_elems, 136862306a36Sopenharmony_ci link_present_elems); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci ieee80211_fragment_element(skb, subelem_len, 137162306a36Sopenharmony_ci IEEE80211_MLE_SUBELEM_FRAGMENT); 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci ieee80211_fragment_element(skb, ml_elem_len, WLAN_EID_FRAGMENT); 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 138062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 138162306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; 138262306a36Sopenharmony_ci struct ieee80211_link_data *link; 138362306a36Sopenharmony_ci struct sk_buff *skb; 138462306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 138562306a36Sopenharmony_ci u8 *pos, qos_info, *ie_start; 138662306a36Sopenharmony_ci size_t offset, noffset; 138762306a36Sopenharmony_ci u16 capab = WLAN_CAPABILITY_ESS, link_capab; 138862306a36Sopenharmony_ci __le16 listen_int; 138962306a36Sopenharmony_ci struct element *ext_capa = NULL; 139062306a36Sopenharmony_ci enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 139162306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = {}; 139262306a36Sopenharmony_ci unsigned int link_id, n_links = 0; 139362306a36Sopenharmony_ci u16 present_elems[PRESENT_ELEMS_MAX] = {}; 139462306a36Sopenharmony_ci void *capab_pos; 139562306a36Sopenharmony_ci size_t size; 139662306a36Sopenharmony_ci int ret; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci /* we know it's writable, cast away the const */ 139962306a36Sopenharmony_ci if (assoc_data->ie_len) 140062306a36Sopenharmony_ci ext_capa = (void *)cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, 140162306a36Sopenharmony_ci assoc_data->ie, 140262306a36Sopenharmony_ci assoc_data->ie_len); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci sdata_assert_lock(sdata); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci size = local->hw.extra_tx_headroom + 140762306a36Sopenharmony_ci sizeof(*mgmt) + /* bit too much but doesn't matter */ 140862306a36Sopenharmony_ci 2 + assoc_data->ssid_len + /* SSID */ 140962306a36Sopenharmony_ci assoc_data->ie_len + /* extra IEs */ 141062306a36Sopenharmony_ci (assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) + 141162306a36Sopenharmony_ci 9; /* WMM */ 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 141462306a36Sopenharmony_ci struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; 141562306a36Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 141662306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (!cbss) 141962306a36Sopenharmony_ci continue; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci sband = local->hw.wiphy->bands[cbss->channel->band]; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci n_links++; 142462306a36Sopenharmony_ci /* add STA profile elements length */ 142562306a36Sopenharmony_ci size += assoc_data->link[link_id].elems_len; 142662306a36Sopenharmony_ci /* and supported rates length */ 142762306a36Sopenharmony_ci size += 4 + sband->n_bitrates; 142862306a36Sopenharmony_ci /* supported channels */ 142962306a36Sopenharmony_ci size += 2 + 2 * sband->n_channels; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci iftd = ieee80211_get_sband_iftype_data(sband, iftype); 143262306a36Sopenharmony_ci if (iftd) 143362306a36Sopenharmony_ci size += iftd->vendor_elems.len; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* power capability */ 143662306a36Sopenharmony_ci size += 4; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* HT, VHT, HE, EHT */ 143962306a36Sopenharmony_ci size += 2 + sizeof(struct ieee80211_ht_cap); 144062306a36Sopenharmony_ci size += 2 + sizeof(struct ieee80211_vht_cap); 144162306a36Sopenharmony_ci size += 2 + 1 + sizeof(struct ieee80211_he_cap_elem) + 144262306a36Sopenharmony_ci sizeof(struct ieee80211_he_mcs_nss_supp) + 144362306a36Sopenharmony_ci IEEE80211_HE_PPE_THRES_MAX_LEN; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (sband->band == NL80211_BAND_6GHZ) 144662306a36Sopenharmony_ci size += 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci size += 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) + 144962306a36Sopenharmony_ci sizeof(struct ieee80211_eht_mcs_nss_supp) + 145062306a36Sopenharmony_ci IEEE80211_EHT_PPE_THRES_MAX_LEN; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci /* non-inheritance element */ 145362306a36Sopenharmony_ci size += 2 + 2 + PRESENT_ELEMS_MAX; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* should be the same across all BSSes */ 145662306a36Sopenharmony_ci if (cbss->capability & WLAN_CAPABILITY_PRIVACY) 145762306a36Sopenharmony_ci capab |= WLAN_CAPABILITY_PRIVACY; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 146162306a36Sopenharmony_ci /* consider the multi-link element with STA profile */ 146262306a36Sopenharmony_ci size += sizeof(struct ieee80211_multi_link_elem); 146362306a36Sopenharmony_ci /* max common info field in basic multi-link element */ 146462306a36Sopenharmony_ci size += sizeof(struct ieee80211_mle_basic_common_info) + 146562306a36Sopenharmony_ci 2 + /* capa & op */ 146662306a36Sopenharmony_ci 2; /* EML capa */ 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci /* 146962306a36Sopenharmony_ci * The capability elements were already considered above; 147062306a36Sopenharmony_ci * note this over-estimates a bit because there's no 147162306a36Sopenharmony_ci * STA profile for the assoc link. 147262306a36Sopenharmony_ci */ 147362306a36Sopenharmony_ci size += (n_links - 1) * 147462306a36Sopenharmony_ci (1 + 1 + /* subelement ID/length */ 147562306a36Sopenharmony_ci 2 + /* STA control */ 147662306a36Sopenharmony_ci 1 + ETH_ALEN + 2 /* STA Info field */); 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci link = sdata_dereference(sdata->link[assoc_data->assoc_link_id], sdata); 148062306a36Sopenharmony_ci if (WARN_ON(!link)) 148162306a36Sopenharmony_ci return -EINVAL; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci if (WARN_ON(!assoc_data->link[assoc_data->assoc_link_id].bss)) 148462306a36Sopenharmony_ci return -EINVAL; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci skb = alloc_skb(size, GFP_KERNEL); 148762306a36Sopenharmony_ci if (!skb) 148862306a36Sopenharmony_ci return -ENOMEM; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM) 149362306a36Sopenharmony_ci capab |= WLAN_CAPABILITY_RADIO_MEASURE; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci /* Set MBSSID support for HE AP if needed */ 149662306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) && 149762306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && 149862306a36Sopenharmony_ci ext_capa && ext_capa->datalen >= 3) 149962306a36Sopenharmony_ci ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci mgmt = skb_put_zero(skb, 24); 150262306a36Sopenharmony_ci memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN); 150362306a36Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 150462306a36Sopenharmony_ci memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci listen_int = cpu_to_le16(assoc_data->s1g ? 150762306a36Sopenharmony_ci ieee80211_encode_usf(local->hw.conf.listen_interval) : 150862306a36Sopenharmony_ci local->hw.conf.listen_interval); 150962306a36Sopenharmony_ci if (!is_zero_ether_addr(assoc_data->prev_ap_addr)) { 151062306a36Sopenharmony_ci skb_put(skb, 10); 151162306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 151262306a36Sopenharmony_ci IEEE80211_STYPE_REASSOC_REQ); 151362306a36Sopenharmony_ci capab_pos = &mgmt->u.reassoc_req.capab_info; 151462306a36Sopenharmony_ci mgmt->u.reassoc_req.listen_interval = listen_int; 151562306a36Sopenharmony_ci memcpy(mgmt->u.reassoc_req.current_ap, 151662306a36Sopenharmony_ci assoc_data->prev_ap_addr, ETH_ALEN); 151762306a36Sopenharmony_ci info.subtype = IEEE80211_STYPE_REASSOC_REQ; 151862306a36Sopenharmony_ci } else { 151962306a36Sopenharmony_ci skb_put(skb, 4); 152062306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 152162306a36Sopenharmony_ci IEEE80211_STYPE_ASSOC_REQ); 152262306a36Sopenharmony_ci capab_pos = &mgmt->u.assoc_req.capab_info; 152362306a36Sopenharmony_ci mgmt->u.assoc_req.listen_interval = listen_int; 152462306a36Sopenharmony_ci info.subtype = IEEE80211_STYPE_ASSOC_REQ; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci /* SSID */ 152862306a36Sopenharmony_ci pos = skb_put(skb, 2 + assoc_data->ssid_len); 152962306a36Sopenharmony_ci ie_start = pos; 153062306a36Sopenharmony_ci *pos++ = WLAN_EID_SSID; 153162306a36Sopenharmony_ci *pos++ = assoc_data->ssid_len; 153262306a36Sopenharmony_ci memcpy(pos, assoc_data->ssid, assoc_data->ssid_len); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci /* add the elements for the assoc (main) link */ 153562306a36Sopenharmony_ci link_capab = capab; 153662306a36Sopenharmony_ci offset = ieee80211_assoc_link_elems(sdata, skb, &link_capab, 153762306a36Sopenharmony_ci ext_capa, 153862306a36Sopenharmony_ci assoc_data->ie, 153962306a36Sopenharmony_ci assoc_data->ie_len, 154062306a36Sopenharmony_ci assoc_data->assoc_link_id, link, 154162306a36Sopenharmony_ci present_elems); 154262306a36Sopenharmony_ci put_unaligned_le16(link_capab, capab_pos); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* if present, add any custom non-vendor IEs */ 154562306a36Sopenharmony_ci if (assoc_data->ie_len) { 154662306a36Sopenharmony_ci noffset = ieee80211_ie_split_vendor(assoc_data->ie, 154762306a36Sopenharmony_ci assoc_data->ie_len, 154862306a36Sopenharmony_ci offset); 154962306a36Sopenharmony_ci skb_put_data(skb, assoc_data->ie + offset, noffset - offset); 155062306a36Sopenharmony_ci offset = noffset; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (assoc_data->wmm) { 155462306a36Sopenharmony_ci if (assoc_data->uapsd) { 155562306a36Sopenharmony_ci qos_info = ifmgd->uapsd_queues; 155662306a36Sopenharmony_ci qos_info |= (ifmgd->uapsd_max_sp_len << 155762306a36Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); 155862306a36Sopenharmony_ci } else { 155962306a36Sopenharmony_ci qos_info = 0; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info); 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci /* add any remaining custom (i.e. vendor specific here) IEs */ 156662306a36Sopenharmony_ci if (assoc_data->ie_len) { 156762306a36Sopenharmony_ci noffset = assoc_data->ie_len; 156862306a36Sopenharmony_ci skb_put_data(skb, assoc_data->ie + offset, noffset - offset); 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (assoc_data->fils_kek_len) { 157262306a36Sopenharmony_ci ret = fils_encrypt_assoc_req(skb, assoc_data); 157362306a36Sopenharmony_ci if (ret < 0) { 157462306a36Sopenharmony_ci dev_kfree_skb(skb); 157562306a36Sopenharmony_ci return ret; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci pos = skb_tail_pointer(skb); 158062306a36Sopenharmony_ci kfree(ifmgd->assoc_req_ies); 158162306a36Sopenharmony_ci ifmgd->assoc_req_ies = kmemdup(ie_start, pos - ie_start, GFP_ATOMIC); 158262306a36Sopenharmony_ci if (!ifmgd->assoc_req_ies) { 158362306a36Sopenharmony_ci dev_kfree_skb(skb); 158462306a36Sopenharmony_ci return -ENOMEM; 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci ifmgd->assoc_req_ies_len = pos - ie_start; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci drv_mgd_prepare_tx(local, sdata, &info); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 159262306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) 159362306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | 159462306a36Sopenharmony_ci IEEE80211_TX_INTFL_MLME_CONN_TX; 159562306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci return 0; 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_civoid ieee80211_send_pspoll(struct ieee80211_local *local, 160162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct ieee80211_pspoll *pspoll; 160462306a36Sopenharmony_ci struct sk_buff *skb; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci skb = ieee80211_pspoll_get(&local->hw, &sdata->vif); 160762306a36Sopenharmony_ci if (!skb) 160862306a36Sopenharmony_ci return; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci pspoll = (struct ieee80211_pspoll *) skb->data; 161162306a36Sopenharmony_ci pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 161462306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 161562306a36Sopenharmony_ci} 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_civoid ieee80211_send_nullfunc(struct ieee80211_local *local, 161862306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 161962306a36Sopenharmony_ci bool powersave) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct sk_buff *skb; 162262306a36Sopenharmony_ci struct ieee80211_hdr_3addr *nullfunc; 162362306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, -1, 162662306a36Sopenharmony_ci !ieee80211_hw_check(&local->hw, 162762306a36Sopenharmony_ci DOESNT_SUPPORT_QOS_NDP)); 162862306a36Sopenharmony_ci if (!skb) 162962306a36Sopenharmony_ci return; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci nullfunc = (struct ieee80211_hdr_3addr *) skb->data; 163262306a36Sopenharmony_ci if (powersave) 163362306a36Sopenharmony_ci nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | 163662306a36Sopenharmony_ci IEEE80211_TX_INTFL_OFFCHAN_TX_OK; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) 163962306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) 164262306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_civoid ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, 164862306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci struct sk_buff *skb; 165162306a36Sopenharmony_ci struct ieee80211_hdr *nullfunc; 165262306a36Sopenharmony_ci __le16 fc; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 165562306a36Sopenharmony_ci return; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); 165862306a36Sopenharmony_ci if (!skb) 165962306a36Sopenharmony_ci return; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci nullfunc = skb_put_zero(skb, 30); 166462306a36Sopenharmony_ci fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | 166562306a36Sopenharmony_ci IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); 166662306a36Sopenharmony_ci nullfunc->frame_control = fc; 166762306a36Sopenharmony_ci memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN); 166862306a36Sopenharmony_ci memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); 166962306a36Sopenharmony_ci memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN); 167062306a36Sopenharmony_ci memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 167362306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; 167462306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci/* spectrum management related things */ 167862306a36Sopenharmony_cistatic void ieee80211_chswitch_work(struct wiphy *wiphy, 167962306a36Sopenharmony_ci struct wiphy_work *work) 168062306a36Sopenharmony_ci{ 168162306a36Sopenharmony_ci struct ieee80211_link_data *link = 168262306a36Sopenharmony_ci container_of(work, struct ieee80211_link_data, 168362306a36Sopenharmony_ci u.mgd.chswitch_work.work); 168462306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 168562306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 168662306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 168762306a36Sopenharmony_ci int ret; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 169062306a36Sopenharmony_ci return; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci sdata_lock(sdata); 169362306a36Sopenharmony_ci mutex_lock(&local->mtx); 169462306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (!ifmgd->associated) 169762306a36Sopenharmony_ci goto out; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci if (!link->conf->csa_active) 170062306a36Sopenharmony_ci goto out; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* 170362306a36Sopenharmony_ci * using reservation isn't immediate as it may be deferred until later 170462306a36Sopenharmony_ci * with multi-vif. once reservation is complete it will re-schedule the 170562306a36Sopenharmony_ci * work with no reserved_chanctx so verify chandef to check if it 170662306a36Sopenharmony_ci * completed successfully 170762306a36Sopenharmony_ci */ 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if (link->reserved_chanctx) { 171062306a36Sopenharmony_ci /* 171162306a36Sopenharmony_ci * with multi-vif csa driver may call ieee80211_csa_finish() 171262306a36Sopenharmony_ci * many times while waiting for other interfaces to use their 171362306a36Sopenharmony_ci * reservations 171462306a36Sopenharmony_ci */ 171562306a36Sopenharmony_ci if (link->reserved_ready) 171662306a36Sopenharmony_ci goto out; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci ret = ieee80211_link_use_reserved_context(link); 171962306a36Sopenharmony_ci if (ret) { 172062306a36Sopenharmony_ci sdata_info(sdata, 172162306a36Sopenharmony_ci "failed to use reserved channel context, disconnecting (err=%d)\n", 172262306a36Sopenharmony_ci ret); 172362306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 172462306a36Sopenharmony_ci &ifmgd->csa_connection_drop_work); 172562306a36Sopenharmony_ci goto out; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci goto out; 172962306a36Sopenharmony_ci } 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (!cfg80211_chandef_identical(&link->conf->chandef, 173262306a36Sopenharmony_ci &link->csa_chandef)) { 173362306a36Sopenharmony_ci sdata_info(sdata, 173462306a36Sopenharmony_ci "failed to finalize channel switch, disconnecting\n"); 173562306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 173662306a36Sopenharmony_ci &ifmgd->csa_connection_drop_work); 173762306a36Sopenharmony_ci goto out; 173862306a36Sopenharmony_ci } 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci link->u.mgd.csa_waiting_bcn = true; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci ieee80211_sta_reset_beacon_monitor(sdata); 174362306a36Sopenharmony_ci ieee80211_sta_reset_conn_monitor(sdata); 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ciout: 174662306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 174762306a36Sopenharmony_ci mutex_unlock(&local->mtx); 174862306a36Sopenharmony_ci sdata_unlock(sdata); 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 175462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 175562306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 175662306a36Sopenharmony_ci int ret; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci sdata_assert_lock(sdata); 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci WARN_ON(!link->conf->csa_active); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (link->csa_block_tx) { 176362306a36Sopenharmony_ci ieee80211_wake_vif_queues(local, sdata, 176462306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_CSA); 176562306a36Sopenharmony_ci link->csa_block_tx = false; 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci link->conf->csa_active = false; 176962306a36Sopenharmony_ci link->u.mgd.csa_waiting_bcn = false; 177062306a36Sopenharmony_ci /* 177162306a36Sopenharmony_ci * If the CSA IE is still present on the beacon after the switch, 177262306a36Sopenharmony_ci * we need to consider it as a new CSA (possibly to self). 177362306a36Sopenharmony_ci */ 177462306a36Sopenharmony_ci link->u.mgd.beacon_crc_valid = false; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci ret = drv_post_channel_switch(sdata); 177762306a36Sopenharmony_ci if (ret) { 177862306a36Sopenharmony_ci sdata_info(sdata, 177962306a36Sopenharmony_ci "driver post channel switch failed, disconnecting\n"); 178062306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 178162306a36Sopenharmony_ci &ifmgd->csa_connection_drop_work); 178262306a36Sopenharmony_ci return; 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef, 0, 0); 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_civoid ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 179162306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) 179462306a36Sopenharmony_ci success = false; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci trace_api_chswitch_done(sdata, success); 179762306a36Sopenharmony_ci if (!success) { 179862306a36Sopenharmony_ci sdata_info(sdata, 179962306a36Sopenharmony_ci "driver channel switch failed, disconnecting\n"); 180062306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 180162306a36Sopenharmony_ci &ifmgd->csa_connection_drop_work); 180262306a36Sopenharmony_ci } else { 180362306a36Sopenharmony_ci wiphy_delayed_work_queue(sdata->local->hw.wiphy, 180462306a36Sopenharmony_ci &sdata->deflink.u.mgd.chswitch_work, 180562306a36Sopenharmony_ci 0); 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_chswitch_done); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_cistatic void 181162306a36Sopenharmony_ciieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 181462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci if (!local->ops->abort_channel_switch) 181762306a36Sopenharmony_ci return; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci mutex_lock(&local->mtx); 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 182262306a36Sopenharmony_ci ieee80211_link_unreserve_chanctx(link); 182362306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (link->csa_block_tx) 182662306a36Sopenharmony_ci ieee80211_wake_vif_queues(local, sdata, 182762306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_CSA); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci link->csa_block_tx = false; 183062306a36Sopenharmony_ci link->conf->csa_active = false; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci mutex_unlock(&local->mtx); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci drv_abort_channel_switch(sdata); 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_cistatic void 183862306a36Sopenharmony_ciieee80211_sta_process_chanswitch(struct ieee80211_link_data *link, 183962306a36Sopenharmony_ci u64 timestamp, u32 device_timestamp, 184062306a36Sopenharmony_ci struct ieee802_11_elems *elems, 184162306a36Sopenharmony_ci bool beacon) 184262306a36Sopenharmony_ci{ 184362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 184462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 184562306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 184662306a36Sopenharmony_ci struct cfg80211_bss *cbss = link->u.mgd.bss; 184762306a36Sopenharmony_ci struct ieee80211_chanctx_conf *conf; 184862306a36Sopenharmony_ci struct ieee80211_chanctx *chanctx; 184962306a36Sopenharmony_ci enum nl80211_band current_band; 185062306a36Sopenharmony_ci struct ieee80211_csa_ie csa_ie; 185162306a36Sopenharmony_ci struct ieee80211_channel_switch ch_switch; 185262306a36Sopenharmony_ci struct ieee80211_bss *bss; 185362306a36Sopenharmony_ci unsigned long timeout; 185462306a36Sopenharmony_ci int res; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci sdata_assert_lock(sdata); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci if (!cbss) 185962306a36Sopenharmony_ci return; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci current_band = cbss->channel->band; 186262306a36Sopenharmony_ci bss = (void *)cbss->priv; 186362306a36Sopenharmony_ci res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band, 186462306a36Sopenharmony_ci bss->vht_cap_info, 186562306a36Sopenharmony_ci link->u.mgd.conn_flags, 186662306a36Sopenharmony_ci link->u.mgd.bssid, &csa_ie); 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci if (!res) { 186962306a36Sopenharmony_ci ch_switch.timestamp = timestamp; 187062306a36Sopenharmony_ci ch_switch.device_timestamp = device_timestamp; 187162306a36Sopenharmony_ci ch_switch.block_tx = csa_ie.mode; 187262306a36Sopenharmony_ci ch_switch.chandef = csa_ie.chandef; 187362306a36Sopenharmony_ci ch_switch.count = csa_ie.count; 187462306a36Sopenharmony_ci ch_switch.delay = csa_ie.max_switch_time; 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci if (res < 0) 187862306a36Sopenharmony_ci goto lock_and_drop_connection; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci if (beacon && link->conf->csa_active && 188162306a36Sopenharmony_ci !link->u.mgd.csa_waiting_bcn) { 188262306a36Sopenharmony_ci if (res) 188362306a36Sopenharmony_ci ieee80211_sta_abort_chanswitch(link); 188462306a36Sopenharmony_ci else 188562306a36Sopenharmony_ci drv_channel_switch_rx_beacon(sdata, &ch_switch); 188662306a36Sopenharmony_ci return; 188762306a36Sopenharmony_ci } else if (link->conf->csa_active || res) { 188862306a36Sopenharmony_ci /* disregard subsequent announcements if already processing */ 188962306a36Sopenharmony_ci return; 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if (link->conf->chandef.chan->band != 189362306a36Sopenharmony_ci csa_ie.chandef.chan->band) { 189462306a36Sopenharmony_ci sdata_info(sdata, 189562306a36Sopenharmony_ci "AP %pM switches to different band (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", 189662306a36Sopenharmony_ci link->u.mgd.bssid, 189762306a36Sopenharmony_ci csa_ie.chandef.chan->center_freq, 189862306a36Sopenharmony_ci csa_ie.chandef.width, csa_ie.chandef.center_freq1, 189962306a36Sopenharmony_ci csa_ie.chandef.center_freq2); 190062306a36Sopenharmony_ci goto lock_and_drop_connection; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, 190462306a36Sopenharmony_ci IEEE80211_CHAN_DISABLED)) { 190562306a36Sopenharmony_ci sdata_info(sdata, 190662306a36Sopenharmony_ci "AP %pM switches to unsupported channel " 190762306a36Sopenharmony_ci "(%d.%03d MHz, width:%d, CF1/2: %d.%03d/%d MHz), " 190862306a36Sopenharmony_ci "disconnecting\n", 190962306a36Sopenharmony_ci link->u.mgd.bssid, 191062306a36Sopenharmony_ci csa_ie.chandef.chan->center_freq, 191162306a36Sopenharmony_ci csa_ie.chandef.chan->freq_offset, 191262306a36Sopenharmony_ci csa_ie.chandef.width, csa_ie.chandef.center_freq1, 191362306a36Sopenharmony_ci csa_ie.chandef.freq1_offset, 191462306a36Sopenharmony_ci csa_ie.chandef.center_freq2); 191562306a36Sopenharmony_ci goto lock_and_drop_connection; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (cfg80211_chandef_identical(&csa_ie.chandef, 191962306a36Sopenharmony_ci &link->conf->chandef) && 192062306a36Sopenharmony_ci (!csa_ie.mode || !beacon)) { 192162306a36Sopenharmony_ci if (link->u.mgd.csa_ignored_same_chan) 192262306a36Sopenharmony_ci return; 192362306a36Sopenharmony_ci sdata_info(sdata, 192462306a36Sopenharmony_ci "AP %pM tries to chanswitch to same channel, ignore\n", 192562306a36Sopenharmony_ci link->u.mgd.bssid); 192662306a36Sopenharmony_ci link->u.mgd.csa_ignored_same_chan = true; 192762306a36Sopenharmony_ci return; 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci /* 193162306a36Sopenharmony_ci * Drop all TDLS peers - either we disconnect or move to a different 193262306a36Sopenharmony_ci * channel from this point on. There's no telling what our peer will do. 193362306a36Sopenharmony_ci * The TDLS WIDER_BW scenario is also problematic, as peers might now 193462306a36Sopenharmony_ci * have an incompatible wider chandef. 193562306a36Sopenharmony_ci */ 193662306a36Sopenharmony_ci ieee80211_teardown_tdls_peers(sdata); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci mutex_lock(&local->mtx); 193962306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 194062306a36Sopenharmony_ci conf = rcu_dereference_protected(link->conf->chanctx_conf, 194162306a36Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 194262306a36Sopenharmony_ci if (!conf) { 194362306a36Sopenharmony_ci sdata_info(sdata, 194462306a36Sopenharmony_ci "no channel context assigned to vif?, disconnecting\n"); 194562306a36Sopenharmony_ci goto drop_connection; 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci chanctx = container_of(conf, struct ieee80211_chanctx, conf); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci if (local->use_chanctx && 195162306a36Sopenharmony_ci !ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) { 195262306a36Sopenharmony_ci sdata_info(sdata, 195362306a36Sopenharmony_ci "driver doesn't support chan-switch with channel contexts\n"); 195462306a36Sopenharmony_ci goto drop_connection; 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci if (drv_pre_channel_switch(sdata, &ch_switch)) { 195862306a36Sopenharmony_ci sdata_info(sdata, 195962306a36Sopenharmony_ci "preparing for channel switch failed, disconnecting\n"); 196062306a36Sopenharmony_ci goto drop_connection; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci res = ieee80211_link_reserve_chanctx(link, &csa_ie.chandef, 196462306a36Sopenharmony_ci chanctx->mode, false); 196562306a36Sopenharmony_ci if (res) { 196662306a36Sopenharmony_ci sdata_info(sdata, 196762306a36Sopenharmony_ci "failed to reserve channel context for channel switch, disconnecting (err=%d)\n", 196862306a36Sopenharmony_ci res); 196962306a36Sopenharmony_ci goto drop_connection; 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci link->conf->csa_active = true; 197462306a36Sopenharmony_ci link->csa_chandef = csa_ie.chandef; 197562306a36Sopenharmony_ci link->csa_block_tx = csa_ie.mode; 197662306a36Sopenharmony_ci link->u.mgd.csa_ignored_same_chan = false; 197762306a36Sopenharmony_ci link->u.mgd.beacon_crc_valid = false; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci if (link->csa_block_tx) 198062306a36Sopenharmony_ci ieee80211_stop_vif_queues(local, sdata, 198162306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_CSA); 198262306a36Sopenharmony_ci mutex_unlock(&local->mtx); 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, 198562306a36Sopenharmony_ci link->link_id, csa_ie.count, 198662306a36Sopenharmony_ci csa_ie.mode, 0); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (local->ops->channel_switch) { 198962306a36Sopenharmony_ci /* use driver's channel switch callback */ 199062306a36Sopenharmony_ci drv_channel_switch(local, sdata, &ch_switch); 199162306a36Sopenharmony_ci return; 199262306a36Sopenharmony_ci } 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci /* channel switch handled in software */ 199562306a36Sopenharmony_ci timeout = TU_TO_JIFFIES((max_t(int, csa_ie.count, 1) - 1) * 199662306a36Sopenharmony_ci cbss->beacon_interval); 199762306a36Sopenharmony_ci wiphy_delayed_work_queue(local->hw.wiphy, 199862306a36Sopenharmony_ci &link->u.mgd.chswitch_work, 199962306a36Sopenharmony_ci timeout); 200062306a36Sopenharmony_ci return; 200162306a36Sopenharmony_ci lock_and_drop_connection: 200262306a36Sopenharmony_ci mutex_lock(&local->mtx); 200362306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 200462306a36Sopenharmony_ci drop_connection: 200562306a36Sopenharmony_ci /* 200662306a36Sopenharmony_ci * This is just so that the disconnect flow will know that 200762306a36Sopenharmony_ci * we were trying to switch channel and failed. In case the 200862306a36Sopenharmony_ci * mode is 1 (we are not allowed to Tx), we will know not to 200962306a36Sopenharmony_ci * send a deauthentication frame. Those two fields will be 201062306a36Sopenharmony_ci * reset when the disconnection worker runs. 201162306a36Sopenharmony_ci */ 201262306a36Sopenharmony_ci link->conf->csa_active = true; 201362306a36Sopenharmony_ci link->csa_block_tx = csa_ie.mode; 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 201662306a36Sopenharmony_ci &ifmgd->csa_connection_drop_work); 201762306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 201862306a36Sopenharmony_ci mutex_unlock(&local->mtx); 201962306a36Sopenharmony_ci} 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_cistatic bool 202262306a36Sopenharmony_ciieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata, 202362306a36Sopenharmony_ci struct ieee80211_channel *channel, 202462306a36Sopenharmony_ci const u8 *country_ie, u8 country_ie_len, 202562306a36Sopenharmony_ci const u8 *pwr_constr_elem, 202662306a36Sopenharmony_ci int *chan_pwr, int *pwr_reduction) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct ieee80211_country_ie_triplet *triplet; 202962306a36Sopenharmony_ci int chan = ieee80211_frequency_to_channel(channel->center_freq); 203062306a36Sopenharmony_ci int i, chan_increment; 203162306a36Sopenharmony_ci bool have_chan_pwr = false; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci /* Invalid IE */ 203462306a36Sopenharmony_ci if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) 203562306a36Sopenharmony_ci return false; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci triplet = (void *)(country_ie + 3); 203862306a36Sopenharmony_ci country_ie_len -= 3; 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci switch (channel->band) { 204162306a36Sopenharmony_ci default: 204262306a36Sopenharmony_ci WARN_ON_ONCE(1); 204362306a36Sopenharmony_ci fallthrough; 204462306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 204562306a36Sopenharmony_ci case NL80211_BAND_60GHZ: 204662306a36Sopenharmony_ci case NL80211_BAND_LC: 204762306a36Sopenharmony_ci chan_increment = 1; 204862306a36Sopenharmony_ci break; 204962306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 205062306a36Sopenharmony_ci chan_increment = 4; 205162306a36Sopenharmony_ci break; 205262306a36Sopenharmony_ci case NL80211_BAND_6GHZ: 205362306a36Sopenharmony_ci /* 205462306a36Sopenharmony_ci * In the 6 GHz band, the "maximum transmit power level" 205562306a36Sopenharmony_ci * field in the triplets is reserved, and thus will be 205662306a36Sopenharmony_ci * zero and we shouldn't use it to control TX power. 205762306a36Sopenharmony_ci * The actual TX power will be given in the transmit 205862306a36Sopenharmony_ci * power envelope element instead. 205962306a36Sopenharmony_ci */ 206062306a36Sopenharmony_ci return false; 206162306a36Sopenharmony_ci } 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci /* find channel */ 206462306a36Sopenharmony_ci while (country_ie_len >= 3) { 206562306a36Sopenharmony_ci u8 first_channel = triplet->chans.first_channel; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID) 206862306a36Sopenharmony_ci goto next; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci for (i = 0; i < triplet->chans.num_channels; i++) { 207162306a36Sopenharmony_ci if (first_channel + i * chan_increment == chan) { 207262306a36Sopenharmony_ci have_chan_pwr = true; 207362306a36Sopenharmony_ci *chan_pwr = triplet->chans.max_power; 207462306a36Sopenharmony_ci break; 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci if (have_chan_pwr) 207862306a36Sopenharmony_ci break; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci next: 208162306a36Sopenharmony_ci triplet++; 208262306a36Sopenharmony_ci country_ie_len -= 3; 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci if (have_chan_pwr && pwr_constr_elem) 208662306a36Sopenharmony_ci *pwr_reduction = *pwr_constr_elem; 208762306a36Sopenharmony_ci else 208862306a36Sopenharmony_ci *pwr_reduction = 0; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci return have_chan_pwr; 209162306a36Sopenharmony_ci} 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_cistatic void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata, 209462306a36Sopenharmony_ci struct ieee80211_channel *channel, 209562306a36Sopenharmony_ci const u8 *cisco_dtpc_ie, 209662306a36Sopenharmony_ci int *pwr_level) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci /* From practical testing, the first data byte of the DTPC element 209962306a36Sopenharmony_ci * seems to contain the requested dBm level, and the CLI on Cisco 210062306a36Sopenharmony_ci * APs clearly state the range is -127 to 127 dBm, which indicates 210162306a36Sopenharmony_ci * a signed byte, although it seemingly never actually goes negative. 210262306a36Sopenharmony_ci * The other byte seems to always be zero. 210362306a36Sopenharmony_ci */ 210462306a36Sopenharmony_ci *pwr_level = (__s8)cisco_dtpc_ie[4]; 210562306a36Sopenharmony_ci} 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_cistatic u64 ieee80211_handle_pwr_constr(struct ieee80211_link_data *link, 210862306a36Sopenharmony_ci struct ieee80211_channel *channel, 210962306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, 211062306a36Sopenharmony_ci const u8 *country_ie, u8 country_ie_len, 211162306a36Sopenharmony_ci const u8 *pwr_constr_ie, 211262306a36Sopenharmony_ci const u8 *cisco_dtpc_ie) 211362306a36Sopenharmony_ci{ 211462306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 211562306a36Sopenharmony_ci bool has_80211h_pwr = false, has_cisco_pwr = false; 211662306a36Sopenharmony_ci int chan_pwr = 0, pwr_reduction_80211h = 0; 211762306a36Sopenharmony_ci int pwr_level_cisco, pwr_level_80211h; 211862306a36Sopenharmony_ci int new_ap_level; 211962306a36Sopenharmony_ci __le16 capab = mgmt->u.probe_resp.capab_info; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci if (ieee80211_is_s1g_beacon(mgmt->frame_control)) 212262306a36Sopenharmony_ci return 0; /* TODO */ 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci if (country_ie && 212562306a36Sopenharmony_ci (capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) || 212662306a36Sopenharmony_ci capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) { 212762306a36Sopenharmony_ci has_80211h_pwr = ieee80211_find_80211h_pwr_constr( 212862306a36Sopenharmony_ci sdata, channel, country_ie, country_ie_len, 212962306a36Sopenharmony_ci pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h); 213062306a36Sopenharmony_ci pwr_level_80211h = 213162306a36Sopenharmony_ci max_t(int, 0, chan_pwr - pwr_reduction_80211h); 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci if (cisco_dtpc_ie) { 213562306a36Sopenharmony_ci ieee80211_find_cisco_dtpc( 213662306a36Sopenharmony_ci sdata, channel, cisco_dtpc_ie, &pwr_level_cisco); 213762306a36Sopenharmony_ci has_cisco_pwr = true; 213862306a36Sopenharmony_ci } 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci if (!has_80211h_pwr && !has_cisco_pwr) 214162306a36Sopenharmony_ci return 0; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci /* If we have both 802.11h and Cisco DTPC, apply both limits 214462306a36Sopenharmony_ci * by picking the smallest of the two power levels advertised. 214562306a36Sopenharmony_ci */ 214662306a36Sopenharmony_ci if (has_80211h_pwr && 214762306a36Sopenharmony_ci (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { 214862306a36Sopenharmony_ci new_ap_level = pwr_level_80211h; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci if (link->ap_power_level == new_ap_level) 215162306a36Sopenharmony_ci return 0; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci sdata_dbg(sdata, 215462306a36Sopenharmony_ci "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", 215562306a36Sopenharmony_ci pwr_level_80211h, chan_pwr, pwr_reduction_80211h, 215662306a36Sopenharmony_ci link->u.mgd.bssid); 215762306a36Sopenharmony_ci } else { /* has_cisco_pwr is always true here. */ 215862306a36Sopenharmony_ci new_ap_level = pwr_level_cisco; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (link->ap_power_level == new_ap_level) 216162306a36Sopenharmony_ci return 0; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci sdata_dbg(sdata, 216462306a36Sopenharmony_ci "Limiting TX power to %d dBm as advertised by %pM\n", 216562306a36Sopenharmony_ci pwr_level_cisco, link->u.mgd.bssid); 216662306a36Sopenharmony_ci } 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci link->ap_power_level = new_ap_level; 216962306a36Sopenharmony_ci if (__ieee80211_recalc_txpower(sdata)) 217062306a36Sopenharmony_ci return BSS_CHANGED_TXPOWER; 217162306a36Sopenharmony_ci return 0; 217262306a36Sopenharmony_ci} 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci/* powersave */ 217562306a36Sopenharmony_cistatic void ieee80211_enable_ps(struct ieee80211_local *local, 217662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 217762306a36Sopenharmony_ci{ 217862306a36Sopenharmony_ci struct ieee80211_conf *conf = &local->hw.conf; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci /* 218162306a36Sopenharmony_ci * If we are scanning right now then the parameters will 218262306a36Sopenharmony_ci * take effect when scan finishes. 218362306a36Sopenharmony_ci */ 218462306a36Sopenharmony_ci if (local->scanning) 218562306a36Sopenharmony_ci return; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (conf->dynamic_ps_timeout > 0 && 218862306a36Sopenharmony_ci !ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) { 218962306a36Sopenharmony_ci mod_timer(&local->dynamic_ps_timer, jiffies + 219062306a36Sopenharmony_ci msecs_to_jiffies(conf->dynamic_ps_timeout)); 219162306a36Sopenharmony_ci } else { 219262306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) 219362306a36Sopenharmony_ci ieee80211_send_nullfunc(local, sdata, true); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && 219662306a36Sopenharmony_ci ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) 219762306a36Sopenharmony_ci return; 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci conf->flags |= IEEE80211_CONF_PS; 220062306a36Sopenharmony_ci ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 220162306a36Sopenharmony_ci } 220262306a36Sopenharmony_ci} 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_cistatic void ieee80211_change_ps(struct ieee80211_local *local) 220562306a36Sopenharmony_ci{ 220662306a36Sopenharmony_ci struct ieee80211_conf *conf = &local->hw.conf; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci if (local->ps_sdata) { 220962306a36Sopenharmony_ci ieee80211_enable_ps(local, local->ps_sdata); 221062306a36Sopenharmony_ci } else if (conf->flags & IEEE80211_CONF_PS) { 221162306a36Sopenharmony_ci conf->flags &= ~IEEE80211_CONF_PS; 221262306a36Sopenharmony_ci ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 221362306a36Sopenharmony_ci del_timer_sync(&local->dynamic_ps_timer); 221462306a36Sopenharmony_ci cancel_work_sync(&local->dynamic_ps_enable_work); 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci} 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_cistatic bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) 221962306a36Sopenharmony_ci{ 222062306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 222162306a36Sopenharmony_ci struct ieee80211_if_managed *mgd = &sdata->u.mgd; 222262306a36Sopenharmony_ci struct sta_info *sta = NULL; 222362306a36Sopenharmony_ci bool authorized = false; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci if (!mgd->powersave) 222662306a36Sopenharmony_ci return false; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci if (mgd->broken_ap) 222962306a36Sopenharmony_ci return false; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (!mgd->associated) 223262306a36Sopenharmony_ci return false; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci if (mgd->flags & IEEE80211_STA_CONNECTION_POLL) 223562306a36Sopenharmony_ci return false; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (!(local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) && 223862306a36Sopenharmony_ci !sdata->deflink.u.mgd.have_beacon) 223962306a36Sopenharmony_ci return false; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci rcu_read_lock(); 224262306a36Sopenharmony_ci sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); 224362306a36Sopenharmony_ci if (sta) 224462306a36Sopenharmony_ci authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); 224562306a36Sopenharmony_ci rcu_read_unlock(); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci return authorized; 224862306a36Sopenharmony_ci} 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci/* need to hold RTNL or interface lock */ 225162306a36Sopenharmony_civoid ieee80211_recalc_ps(struct ieee80211_local *local) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, *found = NULL; 225462306a36Sopenharmony_ci int count = 0; 225562306a36Sopenharmony_ci int timeout; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS) || 225862306a36Sopenharmony_ci ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) { 225962306a36Sopenharmony_ci local->ps_sdata = NULL; 226062306a36Sopenharmony_ci return; 226162306a36Sopenharmony_ci } 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 226462306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 226562306a36Sopenharmony_ci continue; 226662306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) { 226762306a36Sopenharmony_ci /* If an AP vif is found, then disable PS 226862306a36Sopenharmony_ci * by setting the count to zero thereby setting 226962306a36Sopenharmony_ci * ps_sdata to NULL. 227062306a36Sopenharmony_ci */ 227162306a36Sopenharmony_ci count = 0; 227262306a36Sopenharmony_ci break; 227362306a36Sopenharmony_ci } 227462306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION) 227562306a36Sopenharmony_ci continue; 227662306a36Sopenharmony_ci found = sdata; 227762306a36Sopenharmony_ci count++; 227862306a36Sopenharmony_ci } 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci if (count == 1 && ieee80211_powersave_allowed(found)) { 228162306a36Sopenharmony_ci u8 dtimper = found->deflink.u.mgd.dtim_period; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci timeout = local->dynamic_ps_forced_timeout; 228462306a36Sopenharmony_ci if (timeout < 0) 228562306a36Sopenharmony_ci timeout = 100; 228662306a36Sopenharmony_ci local->hw.conf.dynamic_ps_timeout = timeout; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci /* If the TIM IE is invalid, pretend the value is 1 */ 228962306a36Sopenharmony_ci if (!dtimper) 229062306a36Sopenharmony_ci dtimper = 1; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci local->hw.conf.ps_dtim_period = dtimper; 229362306a36Sopenharmony_ci local->ps_sdata = found; 229462306a36Sopenharmony_ci } else { 229562306a36Sopenharmony_ci local->ps_sdata = NULL; 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci ieee80211_change_ps(local); 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_civoid ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci bool ps_allowed = ieee80211_powersave_allowed(sdata); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci if (sdata->vif.cfg.ps != ps_allowed) { 230662306a36Sopenharmony_ci sdata->vif.cfg.ps = ps_allowed; 230762306a36Sopenharmony_ci ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_PS); 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci} 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_civoid ieee80211_dynamic_ps_disable_work(struct work_struct *work) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci struct ieee80211_local *local = 231462306a36Sopenharmony_ci container_of(work, struct ieee80211_local, 231562306a36Sopenharmony_ci dynamic_ps_disable_work); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci if (local->hw.conf.flags & IEEE80211_CONF_PS) { 231862306a36Sopenharmony_ci local->hw.conf.flags &= ~IEEE80211_CONF_PS; 231962306a36Sopenharmony_ci ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci ieee80211_wake_queues_by_reason(&local->hw, 232362306a36Sopenharmony_ci IEEE80211_MAX_QUEUE_MAP, 232462306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_PS, 232562306a36Sopenharmony_ci false); 232662306a36Sopenharmony_ci} 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_civoid ieee80211_dynamic_ps_enable_work(struct work_struct *work) 232962306a36Sopenharmony_ci{ 233062306a36Sopenharmony_ci struct ieee80211_local *local = 233162306a36Sopenharmony_ci container_of(work, struct ieee80211_local, 233262306a36Sopenharmony_ci dynamic_ps_enable_work); 233362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = local->ps_sdata; 233462306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd; 233562306a36Sopenharmony_ci unsigned long flags; 233662306a36Sopenharmony_ci int q; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci /* can only happen when PS was just disabled anyway */ 233962306a36Sopenharmony_ci if (!sdata) 234062306a36Sopenharmony_ci return; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci ifmgd = &sdata->u.mgd; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci if (local->hw.conf.flags & IEEE80211_CONF_PS) 234562306a36Sopenharmony_ci return; 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci if (local->hw.conf.dynamic_ps_timeout > 0) { 234862306a36Sopenharmony_ci /* don't enter PS if TX frames are pending */ 234962306a36Sopenharmony_ci if (drv_tx_frames_pending(local)) { 235062306a36Sopenharmony_ci mod_timer(&local->dynamic_ps_timer, jiffies + 235162306a36Sopenharmony_ci msecs_to_jiffies( 235262306a36Sopenharmony_ci local->hw.conf.dynamic_ps_timeout)); 235362306a36Sopenharmony_ci return; 235462306a36Sopenharmony_ci } 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci /* 235762306a36Sopenharmony_ci * transmission can be stopped by others which leads to 235862306a36Sopenharmony_ci * dynamic_ps_timer expiry. Postpone the ps timer if it 235962306a36Sopenharmony_ci * is not the actual idle state. 236062306a36Sopenharmony_ci */ 236162306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 236262306a36Sopenharmony_ci for (q = 0; q < local->hw.queues; q++) { 236362306a36Sopenharmony_ci if (local->queue_stop_reasons[q]) { 236462306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, 236562306a36Sopenharmony_ci flags); 236662306a36Sopenharmony_ci mod_timer(&local->dynamic_ps_timer, jiffies + 236762306a36Sopenharmony_ci msecs_to_jiffies( 236862306a36Sopenharmony_ci local->hw.conf.dynamic_ps_timeout)); 236962306a36Sopenharmony_ci return; 237062306a36Sopenharmony_ci } 237162306a36Sopenharmony_ci } 237262306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 237362306a36Sopenharmony_ci } 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && 237662306a36Sopenharmony_ci !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { 237762306a36Sopenharmony_ci if (drv_tx_frames_pending(local)) { 237862306a36Sopenharmony_ci mod_timer(&local->dynamic_ps_timer, jiffies + 237962306a36Sopenharmony_ci msecs_to_jiffies( 238062306a36Sopenharmony_ci local->hw.conf.dynamic_ps_timeout)); 238162306a36Sopenharmony_ci } else { 238262306a36Sopenharmony_ci ieee80211_send_nullfunc(local, sdata, true); 238362306a36Sopenharmony_ci /* Flush to get the tx status of nullfunc frame */ 238462306a36Sopenharmony_ci ieee80211_flush_queues(local, sdata, false); 238562306a36Sopenharmony_ci } 238662306a36Sopenharmony_ci } 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci if (!(ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) && 238962306a36Sopenharmony_ci ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) || 239062306a36Sopenharmony_ci (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { 239162306a36Sopenharmony_ci ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED; 239262306a36Sopenharmony_ci local->hw.conf.flags |= IEEE80211_CONF_PS; 239362306a36Sopenharmony_ci ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ci} 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_civoid ieee80211_dynamic_ps_timer(struct timer_list *t) 239862306a36Sopenharmony_ci{ 239962306a36Sopenharmony_ci struct ieee80211_local *local = from_timer(local, t, dynamic_ps_timer); 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); 240262306a36Sopenharmony_ci} 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_civoid ieee80211_dfs_cac_timer_work(struct work_struct *work) 240562306a36Sopenharmony_ci{ 240662306a36Sopenharmony_ci struct delayed_work *delayed_work = to_delayed_work(work); 240762306a36Sopenharmony_ci struct ieee80211_link_data *link = 240862306a36Sopenharmony_ci container_of(delayed_work, struct ieee80211_link_data, 240962306a36Sopenharmony_ci dfs_cac_timer_work); 241062306a36Sopenharmony_ci struct cfg80211_chan_def chandef = link->conf->chandef; 241162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 241462306a36Sopenharmony_ci if (sdata->wdev.cac_started) { 241562306a36Sopenharmony_ci ieee80211_link_release_channel(link); 241662306a36Sopenharmony_ci cfg80211_cac_event(sdata->dev, &chandef, 241762306a36Sopenharmony_ci NL80211_RADAR_CAC_FINISHED, 241862306a36Sopenharmony_ci GFP_KERNEL); 241962306a36Sopenharmony_ci } 242062306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 242162306a36Sopenharmony_ci} 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_cistatic bool 242462306a36Sopenharmony_ci__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) 242562306a36Sopenharmony_ci{ 242662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 242762306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 242862306a36Sopenharmony_ci bool ret = false; 242962306a36Sopenharmony_ci int ac; 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 243262306a36Sopenharmony_ci return false; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 243562306a36Sopenharmony_ci struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; 243662306a36Sopenharmony_ci int non_acm_ac; 243762306a36Sopenharmony_ci unsigned long now = jiffies; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci if (tx_tspec->action == TX_TSPEC_ACTION_NONE && 244062306a36Sopenharmony_ci tx_tspec->admitted_time && 244162306a36Sopenharmony_ci time_after(now, tx_tspec->time_slice_start + HZ)) { 244262306a36Sopenharmony_ci tx_tspec->consumed_tx_time = 0; 244362306a36Sopenharmony_ci tx_tspec->time_slice_start = now; 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci if (tx_tspec->downgraded) 244662306a36Sopenharmony_ci tx_tspec->action = 244762306a36Sopenharmony_ci TX_TSPEC_ACTION_STOP_DOWNGRADE; 244862306a36Sopenharmony_ci } 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci switch (tx_tspec->action) { 245162306a36Sopenharmony_ci case TX_TSPEC_ACTION_STOP_DOWNGRADE: 245262306a36Sopenharmony_ci /* take the original parameters */ 245362306a36Sopenharmony_ci if (drv_conf_tx(local, &sdata->deflink, ac, 245462306a36Sopenharmony_ci &sdata->deflink.tx_conf[ac])) 245562306a36Sopenharmony_ci link_err(&sdata->deflink, 245662306a36Sopenharmony_ci "failed to set TX queue parameters for queue %d\n", 245762306a36Sopenharmony_ci ac); 245862306a36Sopenharmony_ci tx_tspec->action = TX_TSPEC_ACTION_NONE; 245962306a36Sopenharmony_ci tx_tspec->downgraded = false; 246062306a36Sopenharmony_ci ret = true; 246162306a36Sopenharmony_ci break; 246262306a36Sopenharmony_ci case TX_TSPEC_ACTION_DOWNGRADE: 246362306a36Sopenharmony_ci if (time_after(now, tx_tspec->time_slice_start + HZ)) { 246462306a36Sopenharmony_ci tx_tspec->action = TX_TSPEC_ACTION_NONE; 246562306a36Sopenharmony_ci ret = true; 246662306a36Sopenharmony_ci break; 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci /* downgrade next lower non-ACM AC */ 246962306a36Sopenharmony_ci for (non_acm_ac = ac + 1; 247062306a36Sopenharmony_ci non_acm_ac < IEEE80211_NUM_ACS; 247162306a36Sopenharmony_ci non_acm_ac++) 247262306a36Sopenharmony_ci if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac))) 247362306a36Sopenharmony_ci break; 247462306a36Sopenharmony_ci /* Usually the loop will result in using BK even if it 247562306a36Sopenharmony_ci * requires admission control, but such a configuration 247662306a36Sopenharmony_ci * makes no sense and we have to transmit somehow - the 247762306a36Sopenharmony_ci * AC selection does the same thing. 247862306a36Sopenharmony_ci * If we started out trying to downgrade from BK, then 247962306a36Sopenharmony_ci * the extra condition here might be needed. 248062306a36Sopenharmony_ci */ 248162306a36Sopenharmony_ci if (non_acm_ac >= IEEE80211_NUM_ACS) 248262306a36Sopenharmony_ci non_acm_ac = IEEE80211_AC_BK; 248362306a36Sopenharmony_ci if (drv_conf_tx(local, &sdata->deflink, ac, 248462306a36Sopenharmony_ci &sdata->deflink.tx_conf[non_acm_ac])) 248562306a36Sopenharmony_ci link_err(&sdata->deflink, 248662306a36Sopenharmony_ci "failed to set TX queue parameters for queue %d\n", 248762306a36Sopenharmony_ci ac); 248862306a36Sopenharmony_ci tx_tspec->action = TX_TSPEC_ACTION_NONE; 248962306a36Sopenharmony_ci ret = true; 249062306a36Sopenharmony_ci schedule_delayed_work(&ifmgd->tx_tspec_wk, 249162306a36Sopenharmony_ci tx_tspec->time_slice_start + HZ - now + 1); 249262306a36Sopenharmony_ci break; 249362306a36Sopenharmony_ci case TX_TSPEC_ACTION_NONE: 249462306a36Sopenharmony_ci /* nothing now */ 249562306a36Sopenharmony_ci break; 249662306a36Sopenharmony_ci } 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci return ret; 250062306a36Sopenharmony_ci} 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_civoid ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) 250362306a36Sopenharmony_ci{ 250462306a36Sopenharmony_ci if (__ieee80211_sta_handle_tspec_ac_params(sdata)) 250562306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, &sdata->deflink, 250662306a36Sopenharmony_ci BSS_CHANGED_QOS); 250762306a36Sopenharmony_ci} 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_cistatic void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work) 251062306a36Sopenharmony_ci{ 251162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci sdata = container_of(work, struct ieee80211_sub_if_data, 251462306a36Sopenharmony_ci u.mgd.tx_tspec_wk.work); 251562306a36Sopenharmony_ci ieee80211_sta_handle_tspec_ac_params(sdata); 251662306a36Sopenharmony_ci} 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_civoid ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link) 251962306a36Sopenharmony_ci{ 252062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 252162306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 252262306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 252362306a36Sopenharmony_ci struct ieee80211_tx_queue_params *params = link->tx_conf; 252462306a36Sopenharmony_ci u8 ac; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 252762306a36Sopenharmony_ci mlme_dbg(sdata, 252862306a36Sopenharmony_ci "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", 252962306a36Sopenharmony_ci ac, params[ac].acm, 253062306a36Sopenharmony_ci params[ac].aifs, params[ac].cw_min, params[ac].cw_max, 253162306a36Sopenharmony_ci params[ac].txop, params[ac].uapsd, 253262306a36Sopenharmony_ci ifmgd->tx_tspec[ac].downgraded); 253362306a36Sopenharmony_ci if (!ifmgd->tx_tspec[ac].downgraded && 253462306a36Sopenharmony_ci drv_conf_tx(local, link, ac, ¶ms[ac])) 253562306a36Sopenharmony_ci link_err(link, 253662306a36Sopenharmony_ci "failed to set TX queue parameters for AC %d\n", 253762306a36Sopenharmony_ci ac); 253862306a36Sopenharmony_ci } 253962306a36Sopenharmony_ci} 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci/* MLME */ 254262306a36Sopenharmony_cistatic bool 254362306a36Sopenharmony_ciieee80211_sta_wmm_params(struct ieee80211_local *local, 254462306a36Sopenharmony_ci struct ieee80211_link_data *link, 254562306a36Sopenharmony_ci const u8 *wmm_param, size_t wmm_param_len, 254662306a36Sopenharmony_ci const struct ieee80211_mu_edca_param_set *mu_edca) 254762306a36Sopenharmony_ci{ 254862306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 254962306a36Sopenharmony_ci struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS]; 255062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 255162306a36Sopenharmony_ci size_t left; 255262306a36Sopenharmony_ci int count, mu_edca_count, ac; 255362306a36Sopenharmony_ci const u8 *pos; 255462306a36Sopenharmony_ci u8 uapsd_queues = 0; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci if (!local->ops->conf_tx) 255762306a36Sopenharmony_ci return false; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 256062306a36Sopenharmony_ci return false; 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci if (!wmm_param) 256362306a36Sopenharmony_ci return false; 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) 256662306a36Sopenharmony_ci return false; 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) 256962306a36Sopenharmony_ci uapsd_queues = ifmgd->uapsd_queues; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci count = wmm_param[6] & 0x0f; 257262306a36Sopenharmony_ci /* -1 is the initial value of ifmgd->mu_edca_last_param_set. 257362306a36Sopenharmony_ci * if mu_edca was preset before and now it disappeared tell 257462306a36Sopenharmony_ci * the driver about it. 257562306a36Sopenharmony_ci */ 257662306a36Sopenharmony_ci mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1; 257762306a36Sopenharmony_ci if (count == link->u.mgd.wmm_last_param_set && 257862306a36Sopenharmony_ci mu_edca_count == link->u.mgd.mu_edca_last_param_set) 257962306a36Sopenharmony_ci return false; 258062306a36Sopenharmony_ci link->u.mgd.wmm_last_param_set = count; 258162306a36Sopenharmony_ci link->u.mgd.mu_edca_last_param_set = mu_edca_count; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci pos = wmm_param + 8; 258462306a36Sopenharmony_ci left = wmm_param_len - 8; 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci sdata->wmm_acm = 0; 258962306a36Sopenharmony_ci for (; left >= 4; left -= 4, pos += 4) { 259062306a36Sopenharmony_ci int aci = (pos[0] >> 5) & 0x03; 259162306a36Sopenharmony_ci int acm = (pos[0] >> 4) & 0x01; 259262306a36Sopenharmony_ci bool uapsd = false; 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci switch (aci) { 259562306a36Sopenharmony_ci case 1: /* AC_BK */ 259662306a36Sopenharmony_ci ac = IEEE80211_AC_BK; 259762306a36Sopenharmony_ci if (acm) 259862306a36Sopenharmony_ci sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ 259962306a36Sopenharmony_ci if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) 260062306a36Sopenharmony_ci uapsd = true; 260162306a36Sopenharmony_ci params[ac].mu_edca = !!mu_edca; 260262306a36Sopenharmony_ci if (mu_edca) 260362306a36Sopenharmony_ci params[ac].mu_edca_param_rec = mu_edca->ac_bk; 260462306a36Sopenharmony_ci break; 260562306a36Sopenharmony_ci case 2: /* AC_VI */ 260662306a36Sopenharmony_ci ac = IEEE80211_AC_VI; 260762306a36Sopenharmony_ci if (acm) 260862306a36Sopenharmony_ci sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ 260962306a36Sopenharmony_ci if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) 261062306a36Sopenharmony_ci uapsd = true; 261162306a36Sopenharmony_ci params[ac].mu_edca = !!mu_edca; 261262306a36Sopenharmony_ci if (mu_edca) 261362306a36Sopenharmony_ci params[ac].mu_edca_param_rec = mu_edca->ac_vi; 261462306a36Sopenharmony_ci break; 261562306a36Sopenharmony_ci case 3: /* AC_VO */ 261662306a36Sopenharmony_ci ac = IEEE80211_AC_VO; 261762306a36Sopenharmony_ci if (acm) 261862306a36Sopenharmony_ci sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ 261962306a36Sopenharmony_ci if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) 262062306a36Sopenharmony_ci uapsd = true; 262162306a36Sopenharmony_ci params[ac].mu_edca = !!mu_edca; 262262306a36Sopenharmony_ci if (mu_edca) 262362306a36Sopenharmony_ci params[ac].mu_edca_param_rec = mu_edca->ac_vo; 262462306a36Sopenharmony_ci break; 262562306a36Sopenharmony_ci case 0: /* AC_BE */ 262662306a36Sopenharmony_ci default: 262762306a36Sopenharmony_ci ac = IEEE80211_AC_BE; 262862306a36Sopenharmony_ci if (acm) 262962306a36Sopenharmony_ci sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ 263062306a36Sopenharmony_ci if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) 263162306a36Sopenharmony_ci uapsd = true; 263262306a36Sopenharmony_ci params[ac].mu_edca = !!mu_edca; 263362306a36Sopenharmony_ci if (mu_edca) 263462306a36Sopenharmony_ci params[ac].mu_edca_param_rec = mu_edca->ac_be; 263562306a36Sopenharmony_ci break; 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci params[ac].aifs = pos[0] & 0x0f; 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci if (params[ac].aifs < 2) { 264162306a36Sopenharmony_ci link_info(link, 264262306a36Sopenharmony_ci "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n", 264362306a36Sopenharmony_ci params[ac].aifs, aci); 264462306a36Sopenharmony_ci params[ac].aifs = 2; 264562306a36Sopenharmony_ci } 264662306a36Sopenharmony_ci params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4); 264762306a36Sopenharmony_ci params[ac].cw_min = ecw2cw(pos[1] & 0x0f); 264862306a36Sopenharmony_ci params[ac].txop = get_unaligned_le16(pos + 2); 264962306a36Sopenharmony_ci params[ac].acm = acm; 265062306a36Sopenharmony_ci params[ac].uapsd = uapsd; 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci if (params[ac].cw_min == 0 || 265362306a36Sopenharmony_ci params[ac].cw_min > params[ac].cw_max) { 265462306a36Sopenharmony_ci link_info(link, 265562306a36Sopenharmony_ci "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n", 265662306a36Sopenharmony_ci params[ac].cw_min, params[ac].cw_max, aci); 265762306a36Sopenharmony_ci return false; 265862306a36Sopenharmony_ci } 265962306a36Sopenharmony_ci ieee80211_regulatory_limit_wmm_params(sdata, ¶ms[ac], ac); 266062306a36Sopenharmony_ci } 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci /* WMM specification requires all 4 ACIs. */ 266362306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 266462306a36Sopenharmony_ci if (params[ac].cw_min == 0) { 266562306a36Sopenharmony_ci link_info(link, 266662306a36Sopenharmony_ci "AP has invalid WMM params (missing AC %d), using defaults\n", 266762306a36Sopenharmony_ci ac); 266862306a36Sopenharmony_ci return false; 266962306a36Sopenharmony_ci } 267062306a36Sopenharmony_ci } 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 267362306a36Sopenharmony_ci link->tx_conf[ac] = params[ac]; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci ieee80211_mgd_set_link_qos_params(link); 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci /* enable WMM or activate new settings */ 267862306a36Sopenharmony_ci link->conf->qos = true; 267962306a36Sopenharmony_ci return true; 268062306a36Sopenharmony_ci} 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_cistatic void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) 268362306a36Sopenharmony_ci{ 268462306a36Sopenharmony_ci lockdep_assert_held(&sdata->local->mtx); 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL; 268762306a36Sopenharmony_ci ieee80211_run_deferred_scan(sdata->local); 268862306a36Sopenharmony_ci} 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_cistatic void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) 269162306a36Sopenharmony_ci{ 269262306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 269362306a36Sopenharmony_ci __ieee80211_stop_poll(sdata); 269462306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 269562306a36Sopenharmony_ci} 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_cistatic u64 ieee80211_handle_bss_capability(struct ieee80211_link_data *link, 269862306a36Sopenharmony_ci u16 capab, bool erp_valid, u8 erp) 269962306a36Sopenharmony_ci{ 270062306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = link->conf; 270162306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 270262306a36Sopenharmony_ci u64 changed = 0; 270362306a36Sopenharmony_ci bool use_protection; 270462306a36Sopenharmony_ci bool use_short_preamble; 270562306a36Sopenharmony_ci bool use_short_slot; 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci sband = ieee80211_get_link_sband(link); 270862306a36Sopenharmony_ci if (!sband) 270962306a36Sopenharmony_ci return changed; 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci if (erp_valid) { 271262306a36Sopenharmony_ci use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0; 271362306a36Sopenharmony_ci use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0; 271462306a36Sopenharmony_ci } else { 271562306a36Sopenharmony_ci use_protection = false; 271662306a36Sopenharmony_ci use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE); 271762306a36Sopenharmony_ci } 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); 272062306a36Sopenharmony_ci if (sband->band == NL80211_BAND_5GHZ || 272162306a36Sopenharmony_ci sband->band == NL80211_BAND_6GHZ) 272262306a36Sopenharmony_ci use_short_slot = true; 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci if (use_protection != bss_conf->use_cts_prot) { 272562306a36Sopenharmony_ci bss_conf->use_cts_prot = use_protection; 272662306a36Sopenharmony_ci changed |= BSS_CHANGED_ERP_CTS_PROT; 272762306a36Sopenharmony_ci } 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci if (use_short_preamble != bss_conf->use_short_preamble) { 273062306a36Sopenharmony_ci bss_conf->use_short_preamble = use_short_preamble; 273162306a36Sopenharmony_ci changed |= BSS_CHANGED_ERP_PREAMBLE; 273262306a36Sopenharmony_ci } 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci if (use_short_slot != bss_conf->use_short_slot) { 273562306a36Sopenharmony_ci bss_conf->use_short_slot = use_short_slot; 273662306a36Sopenharmony_ci changed |= BSS_CHANGED_ERP_SLOT; 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci return changed; 274062306a36Sopenharmony_ci} 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_cistatic u64 ieee80211_link_set_associated(struct ieee80211_link_data *link, 274362306a36Sopenharmony_ci struct cfg80211_bss *cbss) 274462306a36Sopenharmony_ci{ 274562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 274662306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = link->conf; 274762306a36Sopenharmony_ci struct ieee80211_bss *bss = (void *)cbss->priv; 274862306a36Sopenharmony_ci u64 changed = BSS_CHANGED_QOS; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci /* not really used in MLO */ 275162306a36Sopenharmony_ci sdata->u.mgd.beacon_timeout = 275262306a36Sopenharmony_ci usecs_to_jiffies(ieee80211_tu_to_usec(beacon_loss_count * 275362306a36Sopenharmony_ci bss_conf->beacon_int)); 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci changed |= ieee80211_handle_bss_capability(link, 275662306a36Sopenharmony_ci bss_conf->assoc_capability, 275762306a36Sopenharmony_ci bss->has_erp_value, 275862306a36Sopenharmony_ci bss->erp_value); 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci ieee80211_check_rate_mask(link); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci link->u.mgd.bss = cbss; 276362306a36Sopenharmony_ci memcpy(link->u.mgd.bssid, cbss->bssid, ETH_ALEN); 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci if (sdata->vif.p2p || 276662306a36Sopenharmony_ci sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) { 276762306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci rcu_read_lock(); 277062306a36Sopenharmony_ci ies = rcu_dereference(cbss->ies); 277162306a36Sopenharmony_ci if (ies) { 277262306a36Sopenharmony_ci int ret; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci ret = cfg80211_get_p2p_attr( 277562306a36Sopenharmony_ci ies->data, ies->len, 277662306a36Sopenharmony_ci IEEE80211_P2P_ATTR_ABSENCE_NOTICE, 277762306a36Sopenharmony_ci (u8 *) &bss_conf->p2p_noa_attr, 277862306a36Sopenharmony_ci sizeof(bss_conf->p2p_noa_attr)); 277962306a36Sopenharmony_ci if (ret >= 2) { 278062306a36Sopenharmony_ci link->u.mgd.p2p_noa_index = 278162306a36Sopenharmony_ci bss_conf->p2p_noa_attr.index; 278262306a36Sopenharmony_ci changed |= BSS_CHANGED_P2P_PS; 278362306a36Sopenharmony_ci } 278462306a36Sopenharmony_ci } 278562306a36Sopenharmony_ci rcu_read_unlock(); 278662306a36Sopenharmony_ci } 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci if (link->u.mgd.have_beacon) { 278962306a36Sopenharmony_ci bss_conf->beacon_rate = bss->beacon_rate; 279062306a36Sopenharmony_ci changed |= BSS_CHANGED_BEACON_INFO; 279162306a36Sopenharmony_ci } else { 279262306a36Sopenharmony_ci bss_conf->beacon_rate = NULL; 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci /* Tell the driver to monitor connection quality (if supported) */ 279662306a36Sopenharmony_ci if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI && 279762306a36Sopenharmony_ci bss_conf->cqm_rssi_thold) 279862306a36Sopenharmony_ci changed |= BSS_CHANGED_CQM; 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci return changed; 280162306a36Sopenharmony_ci} 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cistatic void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, 280462306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data, 280562306a36Sopenharmony_ci u64 changed[IEEE80211_MLD_MAX_NUM_LINKS]) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 280862306a36Sopenharmony_ci struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; 280962306a36Sopenharmony_ci u64 vif_changed = BSS_CHANGED_ASSOC; 281062306a36Sopenharmony_ci unsigned int link_id; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci sdata->u.mgd.associated = true; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 281562306a36Sopenharmony_ci struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; 281662306a36Sopenharmony_ci struct ieee80211_link_data *link; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci if (!cbss || 281962306a36Sopenharmony_ci assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) 282062306a36Sopenharmony_ci continue; 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif) && 282362306a36Sopenharmony_ci !(ieee80211_vif_usable_links(&sdata->vif) & BIT(link_id))) 282462306a36Sopenharmony_ci continue; 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 282762306a36Sopenharmony_ci if (WARN_ON(!link)) 282862306a36Sopenharmony_ci return; 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_ci changed[link_id] |= ieee80211_link_set_associated(link, cbss); 283162306a36Sopenharmony_ci } 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci /* just to be sure */ 283462306a36Sopenharmony_ci ieee80211_stop_poll(sdata); 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci ieee80211_led_assoc(local, 1); 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci vif_cfg->assoc = 1; 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci /* Enable ARP filtering */ 284162306a36Sopenharmony_ci if (vif_cfg->arp_addr_cnt) 284262306a36Sopenharmony_ci vif_changed |= BSS_CHANGED_ARP_FILTER; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 284562306a36Sopenharmony_ci for (link_id = 0; 284662306a36Sopenharmony_ci link_id < IEEE80211_MLD_MAX_NUM_LINKS; 284762306a36Sopenharmony_ci link_id++) { 284862306a36Sopenharmony_ci struct ieee80211_link_data *link; 284962306a36Sopenharmony_ci struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci if (!cbss || 285262306a36Sopenharmony_ci !(BIT(link_id) & 285362306a36Sopenharmony_ci ieee80211_vif_usable_links(&sdata->vif)) || 285462306a36Sopenharmony_ci assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) 285562306a36Sopenharmony_ci continue; 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 285862306a36Sopenharmony_ci if (WARN_ON(!link)) 285962306a36Sopenharmony_ci return; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, 286262306a36Sopenharmony_ci changed[link_id]); 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci ieee80211_recalc_smps(sdata, link); 286562306a36Sopenharmony_ci } 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci ieee80211_vif_cfg_change_notify(sdata, vif_changed); 286862306a36Sopenharmony_ci } else { 286962306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, 287062306a36Sopenharmony_ci vif_changed | changed[0]); 287162306a36Sopenharmony_ci } 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci mutex_lock(&local->iflist_mtx); 287462306a36Sopenharmony_ci ieee80211_recalc_ps(local); 287562306a36Sopenharmony_ci mutex_unlock(&local->iflist_mtx); 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci /* leave this here to not change ordering in non-MLO cases */ 287862306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) 287962306a36Sopenharmony_ci ieee80211_recalc_smps(sdata, &sdata->deflink); 288062306a36Sopenharmony_ci ieee80211_recalc_ps_vif(sdata); 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci netif_carrier_on(sdata->dev); 288362306a36Sopenharmony_ci} 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_cistatic void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, 288662306a36Sopenharmony_ci u16 stype, u16 reason, bool tx, 288762306a36Sopenharmony_ci u8 *frame_buf) 288862306a36Sopenharmony_ci{ 288962306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 289062306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 289162306a36Sopenharmony_ci unsigned int link_id; 289262306a36Sopenharmony_ci u64 changed = 0; 289362306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = { 289462306a36Sopenharmony_ci .subtype = stype, 289562306a36Sopenharmony_ci }; 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci sdata_assert_lock(sdata); 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci if (WARN_ON_ONCE(tx && !frame_buf)) 290062306a36Sopenharmony_ci return; 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci if (WARN_ON(!ifmgd->associated)) 290362306a36Sopenharmony_ci return; 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci ieee80211_stop_poll(sdata); 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci ifmgd->associated = false; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci /* other links will be destroyed */ 291062306a36Sopenharmony_ci sdata->deflink.u.mgd.bss = NULL; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci netif_carrier_off(sdata->dev); 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci /* 291562306a36Sopenharmony_ci * if we want to get out of ps before disassoc (why?) we have 291662306a36Sopenharmony_ci * to do it before sending disassoc, as otherwise the null-packet 291762306a36Sopenharmony_ci * won't be valid. 291862306a36Sopenharmony_ci */ 291962306a36Sopenharmony_ci if (local->hw.conf.flags & IEEE80211_CONF_PS) { 292062306a36Sopenharmony_ci local->hw.conf.flags &= ~IEEE80211_CONF_PS; 292162306a36Sopenharmony_ci ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 292262306a36Sopenharmony_ci } 292362306a36Sopenharmony_ci local->ps_sdata = NULL; 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci /* disable per-vif ps */ 292662306a36Sopenharmony_ci ieee80211_recalc_ps_vif(sdata); 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci /* make sure ongoing transmission finishes */ 292962306a36Sopenharmony_ci synchronize_net(); 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci /* 293262306a36Sopenharmony_ci * drop any frame before deauth/disassoc, this can be data or 293362306a36Sopenharmony_ci * management frame. Since we are disconnecting, we should not 293462306a36Sopenharmony_ci * insist sending these frames which can take time and delay 293562306a36Sopenharmony_ci * the disconnection and possible the roaming. 293662306a36Sopenharmony_ci */ 293762306a36Sopenharmony_ci if (tx) 293862306a36Sopenharmony_ci ieee80211_flush_queues(local, sdata, true); 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci /* deauthenticate/disassociate now */ 294162306a36Sopenharmony_ci if (tx || frame_buf) { 294262306a36Sopenharmony_ci /* 294362306a36Sopenharmony_ci * In multi channel scenarios guarantee that the virtual 294462306a36Sopenharmony_ci * interface is granted immediate airtime to transmit the 294562306a36Sopenharmony_ci * deauthentication frame by calling mgd_prepare_tx, if the 294662306a36Sopenharmony_ci * driver requested so. 294762306a36Sopenharmony_ci */ 294862306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, DEAUTH_NEED_MGD_TX_PREP) && 294962306a36Sopenharmony_ci !sdata->deflink.u.mgd.have_beacon) { 295062306a36Sopenharmony_ci drv_mgd_prepare_tx(sdata->local, sdata, &info); 295162306a36Sopenharmony_ci } 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci ieee80211_send_deauth_disassoc(sdata, sdata->vif.cfg.ap_addr, 295462306a36Sopenharmony_ci sdata->vif.cfg.ap_addr, stype, 295562306a36Sopenharmony_ci reason, tx, frame_buf); 295662306a36Sopenharmony_ci } 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci /* flush out frame - make sure the deauth was actually sent */ 295962306a36Sopenharmony_ci if (tx) 296062306a36Sopenharmony_ci ieee80211_flush_queues(local, sdata, false); 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ci drv_mgd_complete_tx(sdata->local, sdata, &info); 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci /* clear AP addr only after building the needed mgmt frames */ 296562306a36Sopenharmony_ci eth_zero_addr(sdata->deflink.u.mgd.bssid); 296662306a36Sopenharmony_ci eth_zero_addr(sdata->vif.cfg.ap_addr); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci sdata->vif.cfg.ssid_len = 0; 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci /* remove AP and TDLS peers */ 297162306a36Sopenharmony_ci sta_info_flush(sdata); 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci /* finally reset all BSS / config parameters */ 297462306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) 297562306a36Sopenharmony_ci changed |= ieee80211_reset_erp_info(sdata); 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci ieee80211_led_assoc(local, 0); 297862306a36Sopenharmony_ci changed |= BSS_CHANGED_ASSOC; 297962306a36Sopenharmony_ci sdata->vif.cfg.assoc = false; 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci sdata->deflink.u.mgd.p2p_noa_index = -1; 298262306a36Sopenharmony_ci memset(&sdata->vif.bss_conf.p2p_noa_attr, 0, 298362306a36Sopenharmony_ci sizeof(sdata->vif.bss_conf.p2p_noa_attr)); 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci /* on the next assoc, re-program HT/VHT parameters */ 298662306a36Sopenharmony_ci memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); 298762306a36Sopenharmony_ci memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); 298862306a36Sopenharmony_ci memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa)); 298962306a36Sopenharmony_ci memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask)); 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci /* 299262306a36Sopenharmony_ci * reset MU-MIMO ownership and group data in default link, 299362306a36Sopenharmony_ci * if used, other links are destroyed 299462306a36Sopenharmony_ci */ 299562306a36Sopenharmony_ci memset(sdata->vif.bss_conf.mu_group.membership, 0, 299662306a36Sopenharmony_ci sizeof(sdata->vif.bss_conf.mu_group.membership)); 299762306a36Sopenharmony_ci memset(sdata->vif.bss_conf.mu_group.position, 0, 299862306a36Sopenharmony_ci sizeof(sdata->vif.bss_conf.mu_group.position)); 299962306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) 300062306a36Sopenharmony_ci changed |= BSS_CHANGED_MU_GROUPS; 300162306a36Sopenharmony_ci sdata->vif.bss_conf.mu_mimo_owner = false; 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_ci sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci del_timer_sync(&local->dynamic_ps_timer); 300662306a36Sopenharmony_ci cancel_work_sync(&local->dynamic_ps_enable_work); 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci /* Disable ARP filtering */ 300962306a36Sopenharmony_ci if (sdata->vif.cfg.arp_addr_cnt) 301062306a36Sopenharmony_ci changed |= BSS_CHANGED_ARP_FILTER; 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci sdata->vif.bss_conf.qos = false; 301362306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) { 301462306a36Sopenharmony_ci changed |= BSS_CHANGED_QOS; 301562306a36Sopenharmony_ci /* The BSSID (not really interesting) and HT changed */ 301662306a36Sopenharmony_ci changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; 301762306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 301862306a36Sopenharmony_ci } else { 301962306a36Sopenharmony_ci ieee80211_vif_cfg_change_notify(sdata, changed); 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci /* disassociated - set to defaults now */ 302362306a36Sopenharmony_ci ieee80211_set_wmm_default(&sdata->deflink, false, false); 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci del_timer_sync(&sdata->u.mgd.conn_mon_timer); 302662306a36Sopenharmony_ci del_timer_sync(&sdata->u.mgd.bcn_mon_timer); 302762306a36Sopenharmony_ci del_timer_sync(&sdata->u.mgd.timer); 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci sdata->vif.bss_conf.dtim_period = 0; 303062306a36Sopenharmony_ci sdata->vif.bss_conf.beacon_rate = NULL; 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci sdata->deflink.u.mgd.have_beacon = false; 303362306a36Sopenharmony_ci sdata->deflink.u.mgd.tracking_signal_avg = false; 303462306a36Sopenharmony_ci sdata->deflink.u.mgd.disable_wmm_tracking = false; 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci ifmgd->flags = 0; 303762306a36Sopenharmony_ci sdata->deflink.u.mgd.conn_flags = 0; 303862306a36Sopenharmony_ci mutex_lock(&local->mtx); 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 304162306a36Sopenharmony_ci struct ieee80211_link_data *link; 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 304462306a36Sopenharmony_ci if (!link) 304562306a36Sopenharmony_ci continue; 304662306a36Sopenharmony_ci ieee80211_link_release_channel(link); 304762306a36Sopenharmony_ci } 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci sdata->vif.bss_conf.csa_active = false; 305062306a36Sopenharmony_ci sdata->deflink.u.mgd.csa_waiting_bcn = false; 305162306a36Sopenharmony_ci sdata->deflink.u.mgd.csa_ignored_same_chan = false; 305262306a36Sopenharmony_ci if (sdata->deflink.csa_block_tx) { 305362306a36Sopenharmony_ci ieee80211_wake_vif_queues(local, sdata, 305462306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_CSA); 305562306a36Sopenharmony_ci sdata->deflink.csa_block_tx = false; 305662306a36Sopenharmony_ci } 305762306a36Sopenharmony_ci mutex_unlock(&local->mtx); 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci /* existing TX TSPEC sessions no longer exist */ 306062306a36Sopenharmony_ci memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec)); 306162306a36Sopenharmony_ci cancel_delayed_work_sync(&ifmgd->tx_tspec_wk); 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci sdata->vif.bss_conf.pwr_reduction = 0; 306462306a36Sopenharmony_ci sdata->vif.bss_conf.tx_pwr_env_num = 0; 306562306a36Sopenharmony_ci memset(sdata->vif.bss_conf.tx_pwr_env, 0, 306662306a36Sopenharmony_ci sizeof(sdata->vif.bss_conf.tx_pwr_env)); 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci ieee80211_vif_set_links(sdata, 0, 0); 306962306a36Sopenharmony_ci} 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_cistatic void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) 307262306a36Sopenharmony_ci{ 307362306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 307462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci mutex_lock(&local->mtx); 307762306a36Sopenharmony_ci if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)) 307862306a36Sopenharmony_ci goto out; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci __ieee80211_stop_poll(sdata); 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci mutex_lock(&local->iflist_mtx); 308362306a36Sopenharmony_ci ieee80211_recalc_ps(local); 308462306a36Sopenharmony_ci mutex_unlock(&local->iflist_mtx); 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) 308762306a36Sopenharmony_ci goto out; 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci /* 309062306a36Sopenharmony_ci * We've received a probe response, but are not sure whether 309162306a36Sopenharmony_ci * we have or will be receiving any beacons or data, so let's 309262306a36Sopenharmony_ci * schedule the timers again, just in case. 309362306a36Sopenharmony_ci */ 309462306a36Sopenharmony_ci ieee80211_sta_reset_beacon_monitor(sdata); 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci mod_timer(&ifmgd->conn_mon_timer, 309762306a36Sopenharmony_ci round_jiffies_up(jiffies + 309862306a36Sopenharmony_ci IEEE80211_CONNECTION_IDLE_TIME)); 309962306a36Sopenharmony_ciout: 310062306a36Sopenharmony_ci mutex_unlock(&local->mtx); 310162306a36Sopenharmony_ci} 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_cistatic void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, 310462306a36Sopenharmony_ci struct ieee80211_hdr *hdr, 310562306a36Sopenharmony_ci u16 tx_time) 310662306a36Sopenharmony_ci{ 310762306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 310862306a36Sopenharmony_ci u16 tid; 310962306a36Sopenharmony_ci int ac; 311062306a36Sopenharmony_ci struct ieee80211_sta_tx_tspec *tx_tspec; 311162306a36Sopenharmony_ci unsigned long now = jiffies; 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci if (!ieee80211_is_data_qos(hdr->frame_control)) 311462306a36Sopenharmony_ci return; 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_ci tid = ieee80211_get_tid(hdr); 311762306a36Sopenharmony_ci ac = ieee80211_ac_from_tid(tid); 311862306a36Sopenharmony_ci tx_tspec = &ifmgd->tx_tspec[ac]; 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci if (likely(!tx_tspec->admitted_time)) 312162306a36Sopenharmony_ci return; 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci if (time_after(now, tx_tspec->time_slice_start + HZ)) { 312462306a36Sopenharmony_ci tx_tspec->consumed_tx_time = 0; 312562306a36Sopenharmony_ci tx_tspec->time_slice_start = now; 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci if (tx_tspec->downgraded) { 312862306a36Sopenharmony_ci tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; 312962306a36Sopenharmony_ci schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); 313062306a36Sopenharmony_ci } 313162306a36Sopenharmony_ci } 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci if (tx_tspec->downgraded) 313462306a36Sopenharmony_ci return; 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci tx_tspec->consumed_tx_time += tx_time; 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_ci if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) { 313962306a36Sopenharmony_ci tx_tspec->downgraded = true; 314062306a36Sopenharmony_ci tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE; 314162306a36Sopenharmony_ci schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); 314262306a36Sopenharmony_ci } 314362306a36Sopenharmony_ci} 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_civoid ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, 314662306a36Sopenharmony_ci struct ieee80211_hdr *hdr, bool ack, u16 tx_time) 314762306a36Sopenharmony_ci{ 314862306a36Sopenharmony_ci ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time); 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci if (!ieee80211_is_any_nullfunc(hdr->frame_control) || 315162306a36Sopenharmony_ci !sdata->u.mgd.probe_send_count) 315262306a36Sopenharmony_ci return; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci if (ack) 315562306a36Sopenharmony_ci sdata->u.mgd.probe_send_count = 0; 315662306a36Sopenharmony_ci else 315762306a36Sopenharmony_ci sdata->u.mgd.nullfunc_failed = true; 315862306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work); 315962306a36Sopenharmony_ci} 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_cistatic void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata, 316262306a36Sopenharmony_ci const u8 *src, const u8 *dst, 316362306a36Sopenharmony_ci const u8 *ssid, size_t ssid_len, 316462306a36Sopenharmony_ci struct ieee80211_channel *channel) 316562306a36Sopenharmony_ci{ 316662306a36Sopenharmony_ci struct sk_buff *skb; 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci skb = ieee80211_build_probe_req(sdata, src, dst, (u32)-1, channel, 316962306a36Sopenharmony_ci ssid, ssid_len, NULL, 0, 317062306a36Sopenharmony_ci IEEE80211_PROBE_FLAG_DIRECTED); 317162306a36Sopenharmony_ci if (skb) 317262306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 317362306a36Sopenharmony_ci} 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_cistatic void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) 317662306a36Sopenharmony_ci{ 317762306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 317862306a36Sopenharmony_ci u8 *dst = sdata->vif.cfg.ap_addr; 317962306a36Sopenharmony_ci u8 unicast_limit = max(1, max_probe_tries - 3); 318062306a36Sopenharmony_ci struct sta_info *sta; 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) 318362306a36Sopenharmony_ci return; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci /* 318662306a36Sopenharmony_ci * Try sending broadcast probe requests for the last three 318762306a36Sopenharmony_ci * probe requests after the first ones failed since some 318862306a36Sopenharmony_ci * buggy APs only support broadcast probe requests. 318962306a36Sopenharmony_ci */ 319062306a36Sopenharmony_ci if (ifmgd->probe_send_count >= unicast_limit) 319162306a36Sopenharmony_ci dst = NULL; 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci /* 319462306a36Sopenharmony_ci * When the hardware reports an accurate Tx ACK status, it's 319562306a36Sopenharmony_ci * better to send a nullfunc frame instead of a probe request, 319662306a36Sopenharmony_ci * as it will kick us off the AP quickly if we aren't associated 319762306a36Sopenharmony_ci * anymore. The timeout will be reset if the frame is ACKed by 319862306a36Sopenharmony_ci * the AP. 319962306a36Sopenharmony_ci */ 320062306a36Sopenharmony_ci ifmgd->probe_send_count++; 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci if (dst) { 320362306a36Sopenharmony_ci mutex_lock(&sdata->local->sta_mtx); 320462306a36Sopenharmony_ci sta = sta_info_get(sdata, dst); 320562306a36Sopenharmony_ci if (!WARN_ON(!sta)) 320662306a36Sopenharmony_ci ieee80211_check_fast_rx(sta); 320762306a36Sopenharmony_ci mutex_unlock(&sdata->local->sta_mtx); 320862306a36Sopenharmony_ci } 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) { 321162306a36Sopenharmony_ci ifmgd->nullfunc_failed = false; 321262306a36Sopenharmony_ci ieee80211_send_nullfunc(sdata->local, sdata, false); 321362306a36Sopenharmony_ci } else { 321462306a36Sopenharmony_ci ieee80211_mlme_send_probe_req(sdata, sdata->vif.addr, dst, 321562306a36Sopenharmony_ci sdata->vif.cfg.ssid, 321662306a36Sopenharmony_ci sdata->vif.cfg.ssid_len, 321762306a36Sopenharmony_ci sdata->deflink.u.mgd.bss->channel); 321862306a36Sopenharmony_ci } 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); 322162306a36Sopenharmony_ci run_again(sdata, ifmgd->probe_timeout); 322262306a36Sopenharmony_ci} 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_cistatic void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, 322562306a36Sopenharmony_ci bool beacon) 322662306a36Sopenharmony_ci{ 322762306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 322862306a36Sopenharmony_ci bool already = false; 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci if (WARN_ON_ONCE(ieee80211_vif_is_mld(&sdata->vif))) 323162306a36Sopenharmony_ci return; 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 323462306a36Sopenharmony_ci return; 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci sdata_lock(sdata); 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci if (!ifmgd->associated) 323962306a36Sopenharmony_ci goto out; 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci if (sdata->local->tmp_channel || sdata->local->scanning) { 324462306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 324562306a36Sopenharmony_ci goto out; 324662306a36Sopenharmony_ci } 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci if (sdata->local->suspending) { 324962306a36Sopenharmony_ci /* reschedule after resume */ 325062306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 325162306a36Sopenharmony_ci ieee80211_reset_ap_probe(sdata); 325262306a36Sopenharmony_ci goto out; 325362306a36Sopenharmony_ci } 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci if (beacon) { 325662306a36Sopenharmony_ci mlme_dbg_ratelimited(sdata, 325762306a36Sopenharmony_ci "detected beacon loss from AP (missed %d beacons) - probing\n", 325862306a36Sopenharmony_ci beacon_loss_count); 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL); 326162306a36Sopenharmony_ci } 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci /* 326462306a36Sopenharmony_ci * The driver/our work has already reported this event or the 326562306a36Sopenharmony_ci * connection monitoring has kicked in and we have already sent 326662306a36Sopenharmony_ci * a probe request. Or maybe the AP died and the driver keeps 326762306a36Sopenharmony_ci * reporting until we disassociate... 326862306a36Sopenharmony_ci * 326962306a36Sopenharmony_ci * In either case we have to ignore the current call to this 327062306a36Sopenharmony_ci * function (except for setting the correct probe reason bit) 327162306a36Sopenharmony_ci * because otherwise we would reset the timer every time and 327262306a36Sopenharmony_ci * never check whether we received a probe response! 327362306a36Sopenharmony_ci */ 327462306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) 327562306a36Sopenharmony_ci already = true; 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci if (already) 328262306a36Sopenharmony_ci goto out; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci mutex_lock(&sdata->local->iflist_mtx); 328562306a36Sopenharmony_ci ieee80211_recalc_ps(sdata->local); 328662306a36Sopenharmony_ci mutex_unlock(&sdata->local->iflist_mtx); 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci ifmgd->probe_send_count = 0; 328962306a36Sopenharmony_ci ieee80211_mgd_probe_ap_send(sdata); 329062306a36Sopenharmony_ci out: 329162306a36Sopenharmony_ci sdata_unlock(sdata); 329262306a36Sopenharmony_ci} 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_cistruct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, 329562306a36Sopenharmony_ci struct ieee80211_vif *vif) 329662306a36Sopenharmony_ci{ 329762306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 329862306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 329962306a36Sopenharmony_ci struct cfg80211_bss *cbss; 330062306a36Sopenharmony_ci struct sk_buff *skb; 330162306a36Sopenharmony_ci const struct element *ssid; 330262306a36Sopenharmony_ci int ssid_len; 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION || 330562306a36Sopenharmony_ci ieee80211_vif_is_mld(&sdata->vif))) 330662306a36Sopenharmony_ci return NULL; 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci sdata_assert_lock(sdata); 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci if (ifmgd->associated) 331162306a36Sopenharmony_ci cbss = sdata->deflink.u.mgd.bss; 331262306a36Sopenharmony_ci else if (ifmgd->auth_data) 331362306a36Sopenharmony_ci cbss = ifmgd->auth_data->bss; 331462306a36Sopenharmony_ci else if (ifmgd->assoc_data && ifmgd->assoc_data->link[0].bss) 331562306a36Sopenharmony_ci cbss = ifmgd->assoc_data->link[0].bss; 331662306a36Sopenharmony_ci else 331762306a36Sopenharmony_ci return NULL; 331862306a36Sopenharmony_ci 331962306a36Sopenharmony_ci rcu_read_lock(); 332062306a36Sopenharmony_ci ssid = ieee80211_bss_get_elem(cbss, WLAN_EID_SSID); 332162306a36Sopenharmony_ci if (WARN_ONCE(!ssid || ssid->datalen > IEEE80211_MAX_SSID_LEN, 332262306a36Sopenharmony_ci "invalid SSID element (len=%d)", 332362306a36Sopenharmony_ci ssid ? ssid->datalen : -1)) 332462306a36Sopenharmony_ci ssid_len = 0; 332562306a36Sopenharmony_ci else 332662306a36Sopenharmony_ci ssid_len = ssid->datalen; 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid, 332962306a36Sopenharmony_ci (u32) -1, cbss->channel, 333062306a36Sopenharmony_ci ssid->data, ssid_len, 333162306a36Sopenharmony_ci NULL, 0, IEEE80211_PROBE_FLAG_DIRECTED); 333262306a36Sopenharmony_ci rcu_read_unlock(); 333362306a36Sopenharmony_ci 333462306a36Sopenharmony_ci return skb; 333562306a36Sopenharmony_ci} 333662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_ap_probereq_get); 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_cistatic void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, 333962306a36Sopenharmony_ci const u8 *buf, size_t len, bool tx, 334062306a36Sopenharmony_ci u16 reason, bool reconnect) 334162306a36Sopenharmony_ci{ 334262306a36Sopenharmony_ci struct ieee80211_event event = { 334362306a36Sopenharmony_ci .type = MLME_EVENT, 334462306a36Sopenharmony_ci .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT, 334562306a36Sopenharmony_ci .u.mlme.reason = reason, 334662306a36Sopenharmony_ci }; 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci if (tx) 334962306a36Sopenharmony_ci cfg80211_tx_mlme_mgmt(sdata->dev, buf, len, reconnect); 335062306a36Sopenharmony_ci else 335162306a36Sopenharmony_ci cfg80211_rx_mlme_mgmt(sdata->dev, buf, len); 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 335462306a36Sopenharmony_ci} 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_cistatic void ___ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) 335762306a36Sopenharmony_ci{ 335862306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 335962306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 336062306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 336162306a36Sopenharmony_ci bool tx; 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci if (!ifmgd->associated) 336462306a36Sopenharmony_ci return; 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci /* in MLO assume we have a link where we can TX the frame */ 336762306a36Sopenharmony_ci tx = ieee80211_vif_is_mld(&sdata->vif) || 336862306a36Sopenharmony_ci !sdata->deflink.csa_block_tx; 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci if (!ifmgd->driver_disconnect) { 337162306a36Sopenharmony_ci unsigned int link_id; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci /* 337462306a36Sopenharmony_ci * AP is probably out of range (or not reachable for another 337562306a36Sopenharmony_ci * reason) so remove the bss structs for that AP. In the case 337662306a36Sopenharmony_ci * of multi-link, it's not clear that all of them really are 337762306a36Sopenharmony_ci * out of range, but if they weren't the driver likely would 337862306a36Sopenharmony_ci * have switched to just have a single link active? 337962306a36Sopenharmony_ci */ 338062306a36Sopenharmony_ci for (link_id = 0; 338162306a36Sopenharmony_ci link_id < ARRAY_SIZE(sdata->link); 338262306a36Sopenharmony_ci link_id++) { 338362306a36Sopenharmony_ci struct ieee80211_link_data *link; 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 338662306a36Sopenharmony_ci if (!link) 338762306a36Sopenharmony_ci continue; 338862306a36Sopenharmony_ci cfg80211_unlink_bss(local->hw.wiphy, link->u.mgd.bss); 338962306a36Sopenharmony_ci link->u.mgd.bss = NULL; 339062306a36Sopenharmony_ci } 339162306a36Sopenharmony_ci } 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, 339462306a36Sopenharmony_ci ifmgd->driver_disconnect ? 339562306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING : 339662306a36Sopenharmony_ci WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, 339762306a36Sopenharmony_ci tx, frame_buf); 339862306a36Sopenharmony_ci mutex_lock(&local->mtx); 339962306a36Sopenharmony_ci /* the other links will be destroyed */ 340062306a36Sopenharmony_ci sdata->vif.bss_conf.csa_active = false; 340162306a36Sopenharmony_ci sdata->deflink.u.mgd.csa_waiting_bcn = false; 340262306a36Sopenharmony_ci if (sdata->deflink.csa_block_tx) { 340362306a36Sopenharmony_ci ieee80211_wake_vif_queues(local, sdata, 340462306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_CSA); 340562306a36Sopenharmony_ci sdata->deflink.csa_block_tx = false; 340662306a36Sopenharmony_ci } 340762306a36Sopenharmony_ci mutex_unlock(&local->mtx); 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx, 341062306a36Sopenharmony_ci WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, 341162306a36Sopenharmony_ci ifmgd->reconnect); 341262306a36Sopenharmony_ci ifmgd->reconnect = false; 341362306a36Sopenharmony_ci} 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_cistatic void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) 341662306a36Sopenharmony_ci{ 341762306a36Sopenharmony_ci sdata_lock(sdata); 341862306a36Sopenharmony_ci ___ieee80211_disconnect(sdata); 341962306a36Sopenharmony_ci sdata_unlock(sdata); 342062306a36Sopenharmony_ci} 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_cistatic void ieee80211_beacon_connection_loss_work(struct wiphy *wiphy, 342362306a36Sopenharmony_ci struct wiphy_work *work) 342462306a36Sopenharmony_ci{ 342562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 342662306a36Sopenharmony_ci container_of(work, struct ieee80211_sub_if_data, 342762306a36Sopenharmony_ci u.mgd.beacon_connection_loss_work); 342862306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci if (ifmgd->connection_loss) { 343162306a36Sopenharmony_ci sdata_info(sdata, "Connection to AP %pM lost\n", 343262306a36Sopenharmony_ci sdata->vif.cfg.ap_addr); 343362306a36Sopenharmony_ci __ieee80211_disconnect(sdata); 343462306a36Sopenharmony_ci ifmgd->connection_loss = false; 343562306a36Sopenharmony_ci } else if (ifmgd->driver_disconnect) { 343662306a36Sopenharmony_ci sdata_info(sdata, 343762306a36Sopenharmony_ci "Driver requested disconnection from AP %pM\n", 343862306a36Sopenharmony_ci sdata->vif.cfg.ap_addr); 343962306a36Sopenharmony_ci __ieee80211_disconnect(sdata); 344062306a36Sopenharmony_ci ifmgd->driver_disconnect = false; 344162306a36Sopenharmony_ci } else { 344262306a36Sopenharmony_ci if (ifmgd->associated) 344362306a36Sopenharmony_ci sdata->deflink.u.mgd.beacon_loss_count++; 344462306a36Sopenharmony_ci ieee80211_mgd_probe_ap(sdata, true); 344562306a36Sopenharmony_ci } 344662306a36Sopenharmony_ci} 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_cistatic void ieee80211_csa_connection_drop_work(struct wiphy *wiphy, 344962306a36Sopenharmony_ci struct wiphy_work *work) 345062306a36Sopenharmony_ci{ 345162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 345262306a36Sopenharmony_ci container_of(work, struct ieee80211_sub_if_data, 345362306a36Sopenharmony_ci u.mgd.csa_connection_drop_work); 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ci __ieee80211_disconnect(sdata); 345662306a36Sopenharmony_ci} 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_civoid ieee80211_beacon_loss(struct ieee80211_vif *vif) 345962306a36Sopenharmony_ci{ 346062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 346162306a36Sopenharmony_ci struct ieee80211_hw *hw = &sdata->local->hw; 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci trace_api_beacon_loss(sdata); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci sdata->u.mgd.connection_loss = false; 346662306a36Sopenharmony_ci wiphy_work_queue(hw->wiphy, &sdata->u.mgd.beacon_connection_loss_work); 346762306a36Sopenharmony_ci} 346862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_beacon_loss); 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_civoid ieee80211_connection_loss(struct ieee80211_vif *vif) 347162306a36Sopenharmony_ci{ 347262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 347362306a36Sopenharmony_ci struct ieee80211_hw *hw = &sdata->local->hw; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci trace_api_connection_loss(sdata); 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci sdata->u.mgd.connection_loss = true; 347862306a36Sopenharmony_ci wiphy_work_queue(hw->wiphy, &sdata->u.mgd.beacon_connection_loss_work); 347962306a36Sopenharmony_ci} 348062306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_connection_loss); 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_civoid ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect) 348362306a36Sopenharmony_ci{ 348462306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 348562306a36Sopenharmony_ci struct ieee80211_hw *hw = &sdata->local->hw; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci trace_api_disconnect(sdata, reconnect); 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 349062306a36Sopenharmony_ci return; 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_ci sdata->u.mgd.driver_disconnect = true; 349362306a36Sopenharmony_ci sdata->u.mgd.reconnect = reconnect; 349462306a36Sopenharmony_ci wiphy_work_queue(hw->wiphy, &sdata->u.mgd.beacon_connection_loss_work); 349562306a36Sopenharmony_ci} 349662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_disconnect); 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_cistatic void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, 349962306a36Sopenharmony_ci bool assoc) 350062306a36Sopenharmony_ci{ 350162306a36Sopenharmony_ci struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci sdata_assert_lock(sdata); 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci if (!assoc) { 350662306a36Sopenharmony_ci /* 350762306a36Sopenharmony_ci * we are not authenticated yet, the only timer that could be 350862306a36Sopenharmony_ci * running is the timeout for the authentication response which 350962306a36Sopenharmony_ci * which is not relevant anymore. 351062306a36Sopenharmony_ci */ 351162306a36Sopenharmony_ci del_timer_sync(&sdata->u.mgd.timer); 351262306a36Sopenharmony_ci sta_info_destroy_addr(sdata, auth_data->ap_addr); 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci /* other links are destroyed */ 351562306a36Sopenharmony_ci sdata->deflink.u.mgd.conn_flags = 0; 351662306a36Sopenharmony_ci eth_zero_addr(sdata->deflink.u.mgd.bssid); 351762306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, &sdata->deflink, 351862306a36Sopenharmony_ci BSS_CHANGED_BSSID); 351962306a36Sopenharmony_ci sdata->u.mgd.flags = 0; 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 352262306a36Sopenharmony_ci ieee80211_link_release_channel(&sdata->deflink); 352362306a36Sopenharmony_ci ieee80211_vif_set_links(sdata, 0, 0); 352462306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 352562306a36Sopenharmony_ci } 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss); 352862306a36Sopenharmony_ci kfree(auth_data); 352962306a36Sopenharmony_ci sdata->u.mgd.auth_data = NULL; 353062306a36Sopenharmony_ci} 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_cienum assoc_status { 353362306a36Sopenharmony_ci ASSOC_SUCCESS, 353462306a36Sopenharmony_ci ASSOC_REJECTED, 353562306a36Sopenharmony_ci ASSOC_TIMEOUT, 353662306a36Sopenharmony_ci ASSOC_ABANDON, 353762306a36Sopenharmony_ci}; 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_cistatic void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, 354062306a36Sopenharmony_ci enum assoc_status status) 354162306a36Sopenharmony_ci{ 354262306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci sdata_assert_lock(sdata); 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci if (status != ASSOC_SUCCESS) { 354762306a36Sopenharmony_ci /* 354862306a36Sopenharmony_ci * we are not associated yet, the only timer that could be 354962306a36Sopenharmony_ci * running is the timeout for the association response which 355062306a36Sopenharmony_ci * which is not relevant anymore. 355162306a36Sopenharmony_ci */ 355262306a36Sopenharmony_ci del_timer_sync(&sdata->u.mgd.timer); 355362306a36Sopenharmony_ci sta_info_destroy_addr(sdata, assoc_data->ap_addr); 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci sdata->deflink.u.mgd.conn_flags = 0; 355662306a36Sopenharmony_ci eth_zero_addr(sdata->deflink.u.mgd.bssid); 355762306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, &sdata->deflink, 355862306a36Sopenharmony_ci BSS_CHANGED_BSSID); 355962306a36Sopenharmony_ci sdata->u.mgd.flags = 0; 356062306a36Sopenharmony_ci sdata->vif.bss_conf.mu_mimo_owner = false; 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_ci if (status != ASSOC_REJECTED) { 356362306a36Sopenharmony_ci struct cfg80211_assoc_failure data = { 356462306a36Sopenharmony_ci .timeout = status == ASSOC_TIMEOUT, 356562306a36Sopenharmony_ci }; 356662306a36Sopenharmony_ci int i; 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(data.bss) != 356962306a36Sopenharmony_ci ARRAY_SIZE(assoc_data->link)); 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(data.bss); i++) 357262306a36Sopenharmony_ci data.bss[i] = assoc_data->link[i].bss; 357362306a36Sopenharmony_ci 357462306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) 357562306a36Sopenharmony_ci data.ap_mld_addr = assoc_data->ap_addr; 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci cfg80211_assoc_failure(sdata->dev, &data); 357862306a36Sopenharmony_ci } 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 358162306a36Sopenharmony_ci ieee80211_link_release_channel(&sdata->deflink); 358262306a36Sopenharmony_ci ieee80211_vif_set_links(sdata, 0, 0); 358362306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 358462306a36Sopenharmony_ci } 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci kfree(assoc_data); 358762306a36Sopenharmony_ci sdata->u.mgd.assoc_data = NULL; 358862306a36Sopenharmony_ci} 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_cistatic void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata, 359162306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len) 359262306a36Sopenharmony_ci{ 359362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 359462306a36Sopenharmony_ci struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data; 359562306a36Sopenharmony_ci const struct element *challenge; 359662306a36Sopenharmony_ci u8 *pos; 359762306a36Sopenharmony_ci u32 tx_flags = 0; 359862306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = { 359962306a36Sopenharmony_ci .subtype = IEEE80211_STYPE_AUTH, 360062306a36Sopenharmony_ci }; 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci pos = mgmt->u.auth.variable; 360362306a36Sopenharmony_ci challenge = cfg80211_find_elem(WLAN_EID_CHALLENGE, pos, 360462306a36Sopenharmony_ci len - (pos - (u8 *)mgmt)); 360562306a36Sopenharmony_ci if (!challenge) 360662306a36Sopenharmony_ci return; 360762306a36Sopenharmony_ci auth_data->expected_transaction = 4; 360862306a36Sopenharmony_ci drv_mgd_prepare_tx(sdata->local, sdata, &info); 360962306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) 361062306a36Sopenharmony_ci tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | 361162306a36Sopenharmony_ci IEEE80211_TX_INTFL_MLME_CONN_TX; 361262306a36Sopenharmony_ci ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0, 361362306a36Sopenharmony_ci (void *)challenge, 361462306a36Sopenharmony_ci challenge->datalen + sizeof(*challenge), 361562306a36Sopenharmony_ci auth_data->ap_addr, auth_data->ap_addr, 361662306a36Sopenharmony_ci auth_data->key, auth_data->key_len, 361762306a36Sopenharmony_ci auth_data->key_idx, tx_flags); 361862306a36Sopenharmony_ci} 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_cistatic bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata) 362162306a36Sopenharmony_ci{ 362262306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 362362306a36Sopenharmony_ci const u8 *ap_addr = ifmgd->auth_data->ap_addr; 362462306a36Sopenharmony_ci struct sta_info *sta; 362562306a36Sopenharmony_ci bool result = true; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci sdata_info(sdata, "authenticated\n"); 362862306a36Sopenharmony_ci ifmgd->auth_data->done = true; 362962306a36Sopenharmony_ci ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; 363062306a36Sopenharmony_ci ifmgd->auth_data->timeout_started = true; 363162306a36Sopenharmony_ci run_again(sdata, ifmgd->auth_data->timeout); 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_ci /* move station state to auth */ 363462306a36Sopenharmony_ci mutex_lock(&sdata->local->sta_mtx); 363562306a36Sopenharmony_ci sta = sta_info_get(sdata, ap_addr); 363662306a36Sopenharmony_ci if (!sta) { 363762306a36Sopenharmony_ci WARN_ONCE(1, "%s: STA %pM not found", sdata->name, ap_addr); 363862306a36Sopenharmony_ci result = false; 363962306a36Sopenharmony_ci goto out; 364062306a36Sopenharmony_ci } 364162306a36Sopenharmony_ci if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) { 364262306a36Sopenharmony_ci sdata_info(sdata, "failed moving %pM to auth\n", ap_addr); 364362306a36Sopenharmony_ci result = false; 364462306a36Sopenharmony_ci goto out; 364562306a36Sopenharmony_ci } 364662306a36Sopenharmony_ci 364762306a36Sopenharmony_ciout: 364862306a36Sopenharmony_ci mutex_unlock(&sdata->local->sta_mtx); 364962306a36Sopenharmony_ci return result; 365062306a36Sopenharmony_ci} 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, 365362306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len) 365462306a36Sopenharmony_ci{ 365562306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 365662306a36Sopenharmony_ci u16 auth_alg, auth_transaction, status_code; 365762306a36Sopenharmony_ci struct ieee80211_event event = { 365862306a36Sopenharmony_ci .type = MLME_EVENT, 365962306a36Sopenharmony_ci .u.mlme.data = AUTH_EVENT, 366062306a36Sopenharmony_ci }; 366162306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = { 366262306a36Sopenharmony_ci .subtype = IEEE80211_STYPE_AUTH, 366362306a36Sopenharmony_ci }; 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci sdata_assert_lock(sdata); 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci if (len < 24 + 6) 366862306a36Sopenharmony_ci return; 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci if (!ifmgd->auth_data || ifmgd->auth_data->done) 367162306a36Sopenharmony_ci return; 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_ci if (!ether_addr_equal(ifmgd->auth_data->ap_addr, mgmt->bssid)) 367462306a36Sopenharmony_ci return; 367562306a36Sopenharmony_ci 367662306a36Sopenharmony_ci auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); 367762306a36Sopenharmony_ci auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); 367862306a36Sopenharmony_ci status_code = le16_to_cpu(mgmt->u.auth.status_code); 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_ci if (auth_alg != ifmgd->auth_data->algorithm || 368162306a36Sopenharmony_ci (auth_alg != WLAN_AUTH_SAE && 368262306a36Sopenharmony_ci auth_transaction != ifmgd->auth_data->expected_transaction) || 368362306a36Sopenharmony_ci (auth_alg == WLAN_AUTH_SAE && 368462306a36Sopenharmony_ci (auth_transaction < ifmgd->auth_data->expected_transaction || 368562306a36Sopenharmony_ci auth_transaction > 2))) { 368662306a36Sopenharmony_ci sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n", 368762306a36Sopenharmony_ci mgmt->sa, auth_alg, ifmgd->auth_data->algorithm, 368862306a36Sopenharmony_ci auth_transaction, 368962306a36Sopenharmony_ci ifmgd->auth_data->expected_transaction); 369062306a36Sopenharmony_ci goto notify_driver; 369162306a36Sopenharmony_ci } 369262306a36Sopenharmony_ci 369362306a36Sopenharmony_ci if (status_code != WLAN_STATUS_SUCCESS) { 369462306a36Sopenharmony_ci cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci if (auth_alg == WLAN_AUTH_SAE && 369762306a36Sopenharmony_ci (status_code == WLAN_STATUS_ANTI_CLOG_REQUIRED || 369862306a36Sopenharmony_ci (auth_transaction == 1 && 369962306a36Sopenharmony_ci (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT || 370062306a36Sopenharmony_ci status_code == WLAN_STATUS_SAE_PK)))) { 370162306a36Sopenharmony_ci /* waiting for userspace now */ 370262306a36Sopenharmony_ci ifmgd->auth_data->waiting = true; 370362306a36Sopenharmony_ci ifmgd->auth_data->timeout = 370462306a36Sopenharmony_ci jiffies + IEEE80211_AUTH_WAIT_SAE_RETRY; 370562306a36Sopenharmony_ci ifmgd->auth_data->timeout_started = true; 370662306a36Sopenharmony_ci run_again(sdata, ifmgd->auth_data->timeout); 370762306a36Sopenharmony_ci goto notify_driver; 370862306a36Sopenharmony_ci } 370962306a36Sopenharmony_ci 371062306a36Sopenharmony_ci sdata_info(sdata, "%pM denied authentication (status %d)\n", 371162306a36Sopenharmony_ci mgmt->sa, status_code); 371262306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, false); 371362306a36Sopenharmony_ci event.u.mlme.status = MLME_DENIED; 371462306a36Sopenharmony_ci event.u.mlme.reason = status_code; 371562306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 371662306a36Sopenharmony_ci goto notify_driver; 371762306a36Sopenharmony_ci } 371862306a36Sopenharmony_ci 371962306a36Sopenharmony_ci switch (ifmgd->auth_data->algorithm) { 372062306a36Sopenharmony_ci case WLAN_AUTH_OPEN: 372162306a36Sopenharmony_ci case WLAN_AUTH_LEAP: 372262306a36Sopenharmony_ci case WLAN_AUTH_FT: 372362306a36Sopenharmony_ci case WLAN_AUTH_SAE: 372462306a36Sopenharmony_ci case WLAN_AUTH_FILS_SK: 372562306a36Sopenharmony_ci case WLAN_AUTH_FILS_SK_PFS: 372662306a36Sopenharmony_ci case WLAN_AUTH_FILS_PK: 372762306a36Sopenharmony_ci break; 372862306a36Sopenharmony_ci case WLAN_AUTH_SHARED_KEY: 372962306a36Sopenharmony_ci if (ifmgd->auth_data->expected_transaction != 4) { 373062306a36Sopenharmony_ci ieee80211_auth_challenge(sdata, mgmt, len); 373162306a36Sopenharmony_ci /* need another frame */ 373262306a36Sopenharmony_ci return; 373362306a36Sopenharmony_ci } 373462306a36Sopenharmony_ci break; 373562306a36Sopenharmony_ci default: 373662306a36Sopenharmony_ci WARN_ONCE(1, "invalid auth alg %d", 373762306a36Sopenharmony_ci ifmgd->auth_data->algorithm); 373862306a36Sopenharmony_ci goto notify_driver; 373962306a36Sopenharmony_ci } 374062306a36Sopenharmony_ci 374162306a36Sopenharmony_ci event.u.mlme.status = MLME_SUCCESS; 374262306a36Sopenharmony_ci info.success = 1; 374362306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 374462306a36Sopenharmony_ci if (ifmgd->auth_data->algorithm != WLAN_AUTH_SAE || 374562306a36Sopenharmony_ci (auth_transaction == 2 && 374662306a36Sopenharmony_ci ifmgd->auth_data->expected_transaction == 2)) { 374762306a36Sopenharmony_ci if (!ieee80211_mark_sta_auth(sdata)) 374862306a36Sopenharmony_ci return; /* ignore frame -- wait for timeout */ 374962306a36Sopenharmony_ci } else if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE && 375062306a36Sopenharmony_ci auth_transaction == 2) { 375162306a36Sopenharmony_ci sdata_info(sdata, "SAE peer confirmed\n"); 375262306a36Sopenharmony_ci ifmgd->auth_data->peer_confirmed = true; 375362306a36Sopenharmony_ci } 375462306a36Sopenharmony_ci 375562306a36Sopenharmony_ci cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); 375662306a36Sopenharmony_cinotify_driver: 375762306a36Sopenharmony_ci drv_mgd_complete_tx(sdata->local, sdata, &info); 375862306a36Sopenharmony_ci} 375962306a36Sopenharmony_ci 376062306a36Sopenharmony_ci#define case_WLAN(type) \ 376162306a36Sopenharmony_ci case WLAN_REASON_##type: return #type 376262306a36Sopenharmony_ci 376362306a36Sopenharmony_ciconst char *ieee80211_get_reason_code_string(u16 reason_code) 376462306a36Sopenharmony_ci{ 376562306a36Sopenharmony_ci switch (reason_code) { 376662306a36Sopenharmony_ci case_WLAN(UNSPECIFIED); 376762306a36Sopenharmony_ci case_WLAN(PREV_AUTH_NOT_VALID); 376862306a36Sopenharmony_ci case_WLAN(DEAUTH_LEAVING); 376962306a36Sopenharmony_ci case_WLAN(DISASSOC_DUE_TO_INACTIVITY); 377062306a36Sopenharmony_ci case_WLAN(DISASSOC_AP_BUSY); 377162306a36Sopenharmony_ci case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA); 377262306a36Sopenharmony_ci case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA); 377362306a36Sopenharmony_ci case_WLAN(DISASSOC_STA_HAS_LEFT); 377462306a36Sopenharmony_ci case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH); 377562306a36Sopenharmony_ci case_WLAN(DISASSOC_BAD_POWER); 377662306a36Sopenharmony_ci case_WLAN(DISASSOC_BAD_SUPP_CHAN); 377762306a36Sopenharmony_ci case_WLAN(INVALID_IE); 377862306a36Sopenharmony_ci case_WLAN(MIC_FAILURE); 377962306a36Sopenharmony_ci case_WLAN(4WAY_HANDSHAKE_TIMEOUT); 378062306a36Sopenharmony_ci case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT); 378162306a36Sopenharmony_ci case_WLAN(IE_DIFFERENT); 378262306a36Sopenharmony_ci case_WLAN(INVALID_GROUP_CIPHER); 378362306a36Sopenharmony_ci case_WLAN(INVALID_PAIRWISE_CIPHER); 378462306a36Sopenharmony_ci case_WLAN(INVALID_AKMP); 378562306a36Sopenharmony_ci case_WLAN(UNSUPP_RSN_VERSION); 378662306a36Sopenharmony_ci case_WLAN(INVALID_RSN_IE_CAP); 378762306a36Sopenharmony_ci case_WLAN(IEEE8021X_FAILED); 378862306a36Sopenharmony_ci case_WLAN(CIPHER_SUITE_REJECTED); 378962306a36Sopenharmony_ci case_WLAN(DISASSOC_UNSPECIFIED_QOS); 379062306a36Sopenharmony_ci case_WLAN(DISASSOC_QAP_NO_BANDWIDTH); 379162306a36Sopenharmony_ci case_WLAN(DISASSOC_LOW_ACK); 379262306a36Sopenharmony_ci case_WLAN(DISASSOC_QAP_EXCEED_TXOP); 379362306a36Sopenharmony_ci case_WLAN(QSTA_LEAVE_QBSS); 379462306a36Sopenharmony_ci case_WLAN(QSTA_NOT_USE); 379562306a36Sopenharmony_ci case_WLAN(QSTA_REQUIRE_SETUP); 379662306a36Sopenharmony_ci case_WLAN(QSTA_TIMEOUT); 379762306a36Sopenharmony_ci case_WLAN(QSTA_CIPHER_NOT_SUPP); 379862306a36Sopenharmony_ci case_WLAN(MESH_PEER_CANCELED); 379962306a36Sopenharmony_ci case_WLAN(MESH_MAX_PEERS); 380062306a36Sopenharmony_ci case_WLAN(MESH_CONFIG); 380162306a36Sopenharmony_ci case_WLAN(MESH_CLOSE); 380262306a36Sopenharmony_ci case_WLAN(MESH_MAX_RETRIES); 380362306a36Sopenharmony_ci case_WLAN(MESH_CONFIRM_TIMEOUT); 380462306a36Sopenharmony_ci case_WLAN(MESH_INVALID_GTK); 380562306a36Sopenharmony_ci case_WLAN(MESH_INCONSISTENT_PARAM); 380662306a36Sopenharmony_ci case_WLAN(MESH_INVALID_SECURITY); 380762306a36Sopenharmony_ci case_WLAN(MESH_PATH_ERROR); 380862306a36Sopenharmony_ci case_WLAN(MESH_PATH_NOFORWARD); 380962306a36Sopenharmony_ci case_WLAN(MESH_PATH_DEST_UNREACHABLE); 381062306a36Sopenharmony_ci case_WLAN(MAC_EXISTS_IN_MBSS); 381162306a36Sopenharmony_ci case_WLAN(MESH_CHAN_REGULATORY); 381262306a36Sopenharmony_ci case_WLAN(MESH_CHAN); 381362306a36Sopenharmony_ci default: return "<unknown>"; 381462306a36Sopenharmony_ci } 381562306a36Sopenharmony_ci} 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, 381862306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len) 381962306a36Sopenharmony_ci{ 382062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 382162306a36Sopenharmony_ci u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); 382262306a36Sopenharmony_ci 382362306a36Sopenharmony_ci sdata_assert_lock(sdata); 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci if (len < 24 + 2) 382662306a36Sopenharmony_ci return; 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_ci if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) { 382962306a36Sopenharmony_ci ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code); 383062306a36Sopenharmony_ci return; 383162306a36Sopenharmony_ci } 383262306a36Sopenharmony_ci 383362306a36Sopenharmony_ci if (ifmgd->associated && 383462306a36Sopenharmony_ci ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) { 383562306a36Sopenharmony_ci sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n", 383662306a36Sopenharmony_ci sdata->vif.cfg.ap_addr, reason_code, 383762306a36Sopenharmony_ci ieee80211_get_reason_code_string(reason_code)); 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, 0, 0, false, NULL); 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, 384262306a36Sopenharmony_ci reason_code, false); 384362306a36Sopenharmony_ci return; 384462306a36Sopenharmony_ci } 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_ci if (ifmgd->assoc_data && 384762306a36Sopenharmony_ci ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->ap_addr)) { 384862306a36Sopenharmony_ci sdata_info(sdata, 384962306a36Sopenharmony_ci "deauthenticated from %pM while associating (Reason: %u=%s)\n", 385062306a36Sopenharmony_ci ifmgd->assoc_data->ap_addr, reason_code, 385162306a36Sopenharmony_ci ieee80211_get_reason_code_string(reason_code)); 385262306a36Sopenharmony_ci 385362306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON); 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); 385662306a36Sopenharmony_ci return; 385762306a36Sopenharmony_ci } 385862306a36Sopenharmony_ci} 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, 386262306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len) 386362306a36Sopenharmony_ci{ 386462306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 386562306a36Sopenharmony_ci u16 reason_code; 386662306a36Sopenharmony_ci 386762306a36Sopenharmony_ci sdata_assert_lock(sdata); 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_ci if (len < 24 + 2) 387062306a36Sopenharmony_ci return; 387162306a36Sopenharmony_ci 387262306a36Sopenharmony_ci if (!ifmgd->associated || 387362306a36Sopenharmony_ci !ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) 387462306a36Sopenharmony_ci return; 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_ci reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) { 387962306a36Sopenharmony_ci ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code); 388062306a36Sopenharmony_ci return; 388162306a36Sopenharmony_ci } 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n", 388462306a36Sopenharmony_ci sdata->vif.cfg.ap_addr, reason_code, 388562306a36Sopenharmony_ci ieee80211_get_reason_code_string(reason_code)); 388662306a36Sopenharmony_ci 388762306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, 0, 0, false, NULL); 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code, 389062306a36Sopenharmony_ci false); 389162306a36Sopenharmony_ci} 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_cistatic void ieee80211_get_rates(struct ieee80211_supported_band *sband, 389462306a36Sopenharmony_ci u8 *supp_rates, unsigned int supp_rates_len, 389562306a36Sopenharmony_ci u32 *rates, u32 *basic_rates, 389662306a36Sopenharmony_ci bool *have_higher_than_11mbit, 389762306a36Sopenharmony_ci int *min_rate, int *min_rate_index, 389862306a36Sopenharmony_ci int shift) 389962306a36Sopenharmony_ci{ 390062306a36Sopenharmony_ci int i, j; 390162306a36Sopenharmony_ci 390262306a36Sopenharmony_ci for (i = 0; i < supp_rates_len; i++) { 390362306a36Sopenharmony_ci int rate = supp_rates[i] & 0x7f; 390462306a36Sopenharmony_ci bool is_basic = !!(supp_rates[i] & 0x80); 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci if ((rate * 5 * (1 << shift)) > 110) 390762306a36Sopenharmony_ci *have_higher_than_11mbit = true; 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci /* 391062306a36Sopenharmony_ci * Skip HT, VHT, HE, EHT and SAE H2E only BSS membership 391162306a36Sopenharmony_ci * selectors since they're not rates. 391262306a36Sopenharmony_ci * 391362306a36Sopenharmony_ci * Note: Even though the membership selector and the basic 391462306a36Sopenharmony_ci * rate flag share the same bit, they are not exactly 391562306a36Sopenharmony_ci * the same. 391662306a36Sopenharmony_ci */ 391762306a36Sopenharmony_ci if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) || 391862306a36Sopenharmony_ci supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY) || 391962306a36Sopenharmony_ci supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY) || 392062306a36Sopenharmony_ci supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_EHT_PHY) || 392162306a36Sopenharmony_ci supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E)) 392262306a36Sopenharmony_ci continue; 392362306a36Sopenharmony_ci 392462306a36Sopenharmony_ci for (j = 0; j < sband->n_bitrates; j++) { 392562306a36Sopenharmony_ci struct ieee80211_rate *br; 392662306a36Sopenharmony_ci int brate; 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci br = &sband->bitrates[j]; 392962306a36Sopenharmony_ci 393062306a36Sopenharmony_ci brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); 393162306a36Sopenharmony_ci if (brate == rate) { 393262306a36Sopenharmony_ci *rates |= BIT(j); 393362306a36Sopenharmony_ci if (is_basic) 393462306a36Sopenharmony_ci *basic_rates |= BIT(j); 393562306a36Sopenharmony_ci if ((rate * 5) < *min_rate) { 393662306a36Sopenharmony_ci *min_rate = rate * 5; 393762306a36Sopenharmony_ci *min_rate_index = j; 393862306a36Sopenharmony_ci } 393962306a36Sopenharmony_ci break; 394062306a36Sopenharmony_ci } 394162306a36Sopenharmony_ci } 394262306a36Sopenharmony_ci } 394362306a36Sopenharmony_ci} 394462306a36Sopenharmony_ci 394562306a36Sopenharmony_cistatic bool ieee80211_twt_req_supported(struct ieee80211_sub_if_data *sdata, 394662306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 394762306a36Sopenharmony_ci const struct link_sta_info *link_sta, 394862306a36Sopenharmony_ci const struct ieee802_11_elems *elems) 394962306a36Sopenharmony_ci{ 395062306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *own_he_cap = 395162306a36Sopenharmony_ci ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 395262306a36Sopenharmony_ci 395362306a36Sopenharmony_ci if (elems->ext_capab_len < 10) 395462306a36Sopenharmony_ci return false; 395562306a36Sopenharmony_ci 395662306a36Sopenharmony_ci if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT)) 395762306a36Sopenharmony_ci return false; 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci return link_sta->pub->he_cap.he_cap_elem.mac_cap_info[0] & 396062306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP0_TWT_RES && 396162306a36Sopenharmony_ci own_he_cap && 396262306a36Sopenharmony_ci (own_he_cap->he_cap_elem.mac_cap_info[0] & 396362306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP0_TWT_REQ); 396462306a36Sopenharmony_ci} 396562306a36Sopenharmony_ci 396662306a36Sopenharmony_cistatic u64 ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata, 396762306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 396862306a36Sopenharmony_ci struct ieee80211_link_data *link, 396962306a36Sopenharmony_ci struct link_sta_info *link_sta, 397062306a36Sopenharmony_ci struct ieee802_11_elems *elems) 397162306a36Sopenharmony_ci{ 397262306a36Sopenharmony_ci bool twt = ieee80211_twt_req_supported(sdata, sband, link_sta, elems); 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci if (link->conf->twt_requester != twt) { 397562306a36Sopenharmony_ci link->conf->twt_requester = twt; 397662306a36Sopenharmony_ci return BSS_CHANGED_TWT; 397762306a36Sopenharmony_ci } 397862306a36Sopenharmony_ci return 0; 397962306a36Sopenharmony_ci} 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_cistatic bool ieee80211_twt_bcast_support(struct ieee80211_sub_if_data *sdata, 398262306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, 398362306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 398462306a36Sopenharmony_ci struct link_sta_info *link_sta) 398562306a36Sopenharmony_ci{ 398662306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *own_he_cap = 398762306a36Sopenharmony_ci ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 398862306a36Sopenharmony_ci 398962306a36Sopenharmony_ci return bss_conf->he_support && 399062306a36Sopenharmony_ci (link_sta->pub->he_cap.he_cap_elem.mac_cap_info[2] & 399162306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP2_BCAST_TWT) && 399262306a36Sopenharmony_ci own_he_cap && 399362306a36Sopenharmony_ci (own_he_cap->he_cap_elem.mac_cap_info[2] & 399462306a36Sopenharmony_ci IEEE80211_HE_MAC_CAP2_BCAST_TWT); 399562306a36Sopenharmony_ci} 399662306a36Sopenharmony_ci 399762306a36Sopenharmony_cistatic bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, 399862306a36Sopenharmony_ci struct link_sta_info *link_sta, 399962306a36Sopenharmony_ci struct cfg80211_bss *cbss, 400062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, 400162306a36Sopenharmony_ci const u8 *elem_start, 400262306a36Sopenharmony_ci unsigned int elem_len, 400362306a36Sopenharmony_ci u64 *changed) 400462306a36Sopenharmony_ci{ 400562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 400662306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; 400762306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = link->conf; 400862306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 400962306a36Sopenharmony_ci unsigned int link_id = link->link_id; 401062306a36Sopenharmony_ci struct ieee80211_elems_parse_params parse_params = { 401162306a36Sopenharmony_ci .start = elem_start, 401262306a36Sopenharmony_ci .len = elem_len, 401362306a36Sopenharmony_ci .link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id, 401462306a36Sopenharmony_ci .from_ap = true, 401562306a36Sopenharmony_ci }; 401662306a36Sopenharmony_ci bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; 401762306a36Sopenharmony_ci bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ; 401862306a36Sopenharmony_ci const struct cfg80211_bss_ies *bss_ies = NULL; 401962306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 402062306a36Sopenharmony_ci struct ieee802_11_elems *elems; 402162306a36Sopenharmony_ci const __le16 prof_bss_param_ch_present = 402262306a36Sopenharmony_ci cpu_to_le16(IEEE80211_MLE_STA_CONTROL_BSS_PARAM_CHANGE_CNT_PRESENT); 402362306a36Sopenharmony_ci u16 capab_info; 402462306a36Sopenharmony_ci bool ret; 402562306a36Sopenharmony_ci 402662306a36Sopenharmony_ci elems = ieee802_11_parse_elems_full(&parse_params); 402762306a36Sopenharmony_ci if (!elems) 402862306a36Sopenharmony_ci return false; 402962306a36Sopenharmony_ci 403062306a36Sopenharmony_ci if (link_id == assoc_data->assoc_link_id) { 403162306a36Sopenharmony_ci capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); 403262306a36Sopenharmony_ci 403362306a36Sopenharmony_ci /* 403462306a36Sopenharmony_ci * we should not get to this flow unless the association was 403562306a36Sopenharmony_ci * successful, so set the status directly to success 403662306a36Sopenharmony_ci */ 403762306a36Sopenharmony_ci assoc_data->link[link_id].status = WLAN_STATUS_SUCCESS; 403862306a36Sopenharmony_ci if (elems->ml_basic) { 403962306a36Sopenharmony_ci if (!(elems->ml_basic->control & 404062306a36Sopenharmony_ci cpu_to_le16(IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT))) { 404162306a36Sopenharmony_ci ret = false; 404262306a36Sopenharmony_ci goto out; 404362306a36Sopenharmony_ci } 404462306a36Sopenharmony_ci link->u.mgd.bss_param_ch_cnt = 404562306a36Sopenharmony_ci ieee80211_mle_get_bss_param_ch_cnt(elems->ml_basic); 404662306a36Sopenharmony_ci } 404762306a36Sopenharmony_ci } else if (!elems->prof || 404862306a36Sopenharmony_ci !(elems->prof->control & prof_bss_param_ch_present)) { 404962306a36Sopenharmony_ci ret = false; 405062306a36Sopenharmony_ci goto out; 405162306a36Sopenharmony_ci } else { 405262306a36Sopenharmony_ci const u8 *ptr = elems->prof->variable + 405362306a36Sopenharmony_ci elems->prof->sta_info_len - 1; 405462306a36Sopenharmony_ci 405562306a36Sopenharmony_ci /* 405662306a36Sopenharmony_ci * During parsing, we validated that these fields exist, 405762306a36Sopenharmony_ci * otherwise elems->prof would have been set to NULL. 405862306a36Sopenharmony_ci */ 405962306a36Sopenharmony_ci capab_info = get_unaligned_le16(ptr); 406062306a36Sopenharmony_ci assoc_data->link[link_id].status = get_unaligned_le16(ptr + 2); 406162306a36Sopenharmony_ci link->u.mgd.bss_param_ch_cnt = 406262306a36Sopenharmony_ci ieee80211_mle_basic_sta_prof_bss_param_ch_cnt(elems->prof); 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_ci if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) { 406562306a36Sopenharmony_ci link_info(link, "association response status code=%u\n", 406662306a36Sopenharmony_ci assoc_data->link[link_id].status); 406762306a36Sopenharmony_ci ret = true; 406862306a36Sopenharmony_ci goto out; 406962306a36Sopenharmony_ci } 407062306a36Sopenharmony_ci } 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci if (!is_s1g && !elems->supp_rates) { 407362306a36Sopenharmony_ci sdata_info(sdata, "no SuppRates element in AssocResp\n"); 407462306a36Sopenharmony_ci ret = false; 407562306a36Sopenharmony_ci goto out; 407662306a36Sopenharmony_ci } 407762306a36Sopenharmony_ci 407862306a36Sopenharmony_ci link->u.mgd.tdls_chan_switch_prohibited = 407962306a36Sopenharmony_ci elems->ext_capab && elems->ext_capab_len >= 5 && 408062306a36Sopenharmony_ci (elems->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED); 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci /* 408362306a36Sopenharmony_ci * Some APs are erroneously not including some information in their 408462306a36Sopenharmony_ci * (re)association response frames. Try to recover by using the data 408562306a36Sopenharmony_ci * from the beacon or probe response. This seems to afflict mobile 408662306a36Sopenharmony_ci * 2G/3G/4G wifi routers, reported models include the "Onda PN51T", 408762306a36Sopenharmony_ci * "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device. 408862306a36Sopenharmony_ci */ 408962306a36Sopenharmony_ci if (!is_6ghz && 409062306a36Sopenharmony_ci ((assoc_data->wmm && !elems->wmm_param) || 409162306a36Sopenharmony_ci (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && 409262306a36Sopenharmony_ci (!elems->ht_cap_elem || !elems->ht_operation)) || 409362306a36Sopenharmony_ci (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && 409462306a36Sopenharmony_ci (!elems->vht_cap_elem || !elems->vht_operation)))) { 409562306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 409662306a36Sopenharmony_ci struct ieee802_11_elems *bss_elems; 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci rcu_read_lock(); 409962306a36Sopenharmony_ci ies = rcu_dereference(cbss->ies); 410062306a36Sopenharmony_ci if (ies) 410162306a36Sopenharmony_ci bss_ies = kmemdup(ies, sizeof(*ies) + ies->len, 410262306a36Sopenharmony_ci GFP_ATOMIC); 410362306a36Sopenharmony_ci rcu_read_unlock(); 410462306a36Sopenharmony_ci if (!bss_ies) { 410562306a36Sopenharmony_ci ret = false; 410662306a36Sopenharmony_ci goto out; 410762306a36Sopenharmony_ci } 410862306a36Sopenharmony_ci 410962306a36Sopenharmony_ci parse_params.start = bss_ies->data; 411062306a36Sopenharmony_ci parse_params.len = bss_ies->len; 411162306a36Sopenharmony_ci parse_params.bss = cbss; 411262306a36Sopenharmony_ci bss_elems = ieee802_11_parse_elems_full(&parse_params); 411362306a36Sopenharmony_ci if (!bss_elems) { 411462306a36Sopenharmony_ci ret = false; 411562306a36Sopenharmony_ci goto out; 411662306a36Sopenharmony_ci } 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci if (assoc_data->wmm && 411962306a36Sopenharmony_ci !elems->wmm_param && bss_elems->wmm_param) { 412062306a36Sopenharmony_ci elems->wmm_param = bss_elems->wmm_param; 412162306a36Sopenharmony_ci sdata_info(sdata, 412262306a36Sopenharmony_ci "AP bug: WMM param missing from AssocResp\n"); 412362306a36Sopenharmony_ci } 412462306a36Sopenharmony_ci 412562306a36Sopenharmony_ci /* 412662306a36Sopenharmony_ci * Also check if we requested HT/VHT, otherwise the AP doesn't 412762306a36Sopenharmony_ci * have to include the IEs in the (re)association response. 412862306a36Sopenharmony_ci */ 412962306a36Sopenharmony_ci if (!elems->ht_cap_elem && bss_elems->ht_cap_elem && 413062306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) { 413162306a36Sopenharmony_ci elems->ht_cap_elem = bss_elems->ht_cap_elem; 413262306a36Sopenharmony_ci sdata_info(sdata, 413362306a36Sopenharmony_ci "AP bug: HT capability missing from AssocResp\n"); 413462306a36Sopenharmony_ci } 413562306a36Sopenharmony_ci if (!elems->ht_operation && bss_elems->ht_operation && 413662306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) { 413762306a36Sopenharmony_ci elems->ht_operation = bss_elems->ht_operation; 413862306a36Sopenharmony_ci sdata_info(sdata, 413962306a36Sopenharmony_ci "AP bug: HT operation missing from AssocResp\n"); 414062306a36Sopenharmony_ci } 414162306a36Sopenharmony_ci if (!elems->vht_cap_elem && bss_elems->vht_cap_elem && 414262306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { 414362306a36Sopenharmony_ci elems->vht_cap_elem = bss_elems->vht_cap_elem; 414462306a36Sopenharmony_ci sdata_info(sdata, 414562306a36Sopenharmony_ci "AP bug: VHT capa missing from AssocResp\n"); 414662306a36Sopenharmony_ci } 414762306a36Sopenharmony_ci if (!elems->vht_operation && bss_elems->vht_operation && 414862306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { 414962306a36Sopenharmony_ci elems->vht_operation = bss_elems->vht_operation; 415062306a36Sopenharmony_ci sdata_info(sdata, 415162306a36Sopenharmony_ci "AP bug: VHT operation missing from AssocResp\n"); 415262306a36Sopenharmony_ci } 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_ci kfree(bss_elems); 415562306a36Sopenharmony_ci } 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci /* 415862306a36Sopenharmony_ci * We previously checked these in the beacon/probe response, so 415962306a36Sopenharmony_ci * they should be present here. This is just a safety net. 416062306a36Sopenharmony_ci */ 416162306a36Sopenharmony_ci if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) && 416262306a36Sopenharmony_ci (!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) { 416362306a36Sopenharmony_ci sdata_info(sdata, 416462306a36Sopenharmony_ci "HT AP is missing WMM params or HT capability/operation\n"); 416562306a36Sopenharmony_ci ret = false; 416662306a36Sopenharmony_ci goto out; 416762306a36Sopenharmony_ci } 416862306a36Sopenharmony_ci 416962306a36Sopenharmony_ci if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) && 417062306a36Sopenharmony_ci (!elems->vht_cap_elem || !elems->vht_operation)) { 417162306a36Sopenharmony_ci sdata_info(sdata, 417262306a36Sopenharmony_ci "VHT AP is missing VHT capability/operation\n"); 417362306a36Sopenharmony_ci ret = false; 417462306a36Sopenharmony_ci goto out; 417562306a36Sopenharmony_ci } 417662306a36Sopenharmony_ci 417762306a36Sopenharmony_ci if (is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && 417862306a36Sopenharmony_ci !elems->he_6ghz_capa) { 417962306a36Sopenharmony_ci sdata_info(sdata, 418062306a36Sopenharmony_ci "HE 6 GHz AP is missing HE 6 GHz band capability\n"); 418162306a36Sopenharmony_ci ret = false; 418262306a36Sopenharmony_ci goto out; 418362306a36Sopenharmony_ci } 418462306a36Sopenharmony_ci 418562306a36Sopenharmony_ci if (WARN_ON(!link->conf->chandef.chan)) { 418662306a36Sopenharmony_ci ret = false; 418762306a36Sopenharmony_ci goto out; 418862306a36Sopenharmony_ci } 418962306a36Sopenharmony_ci sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; 419062306a36Sopenharmony_ci 419162306a36Sopenharmony_ci if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && 419262306a36Sopenharmony_ci (!elems->he_cap || !elems->he_operation)) { 419362306a36Sopenharmony_ci sdata_info(sdata, 419462306a36Sopenharmony_ci "HE AP is missing HE capability/operation\n"); 419562306a36Sopenharmony_ci ret = false; 419662306a36Sopenharmony_ci goto out; 419762306a36Sopenharmony_ci } 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci /* Set up internal HT/VHT capabilities */ 420062306a36Sopenharmony_ci if (elems->ht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)) 420162306a36Sopenharmony_ci ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, 420262306a36Sopenharmony_ci elems->ht_cap_elem, 420362306a36Sopenharmony_ci link_sta); 420462306a36Sopenharmony_ci 420562306a36Sopenharmony_ci if (elems->vht_cap_elem && 420662306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { 420762306a36Sopenharmony_ci const struct ieee80211_vht_cap *bss_vht_cap = NULL; 420862306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_ci /* 421162306a36Sopenharmony_ci * Cisco AP module 9115 with FW 17.3 has a bug and sends a 421262306a36Sopenharmony_ci * too large maximum MPDU length in the association response 421362306a36Sopenharmony_ci * (indicating 12k) that it cannot actually process ... 421462306a36Sopenharmony_ci * Work around that. 421562306a36Sopenharmony_ci */ 421662306a36Sopenharmony_ci rcu_read_lock(); 421762306a36Sopenharmony_ci ies = rcu_dereference(cbss->ies); 421862306a36Sopenharmony_ci if (ies) { 421962306a36Sopenharmony_ci const struct element *elem; 422062306a36Sopenharmony_ci 422162306a36Sopenharmony_ci elem = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY, 422262306a36Sopenharmony_ci ies->data, ies->len); 422362306a36Sopenharmony_ci if (elem && elem->datalen >= sizeof(*bss_vht_cap)) 422462306a36Sopenharmony_ci bss_vht_cap = (const void *)elem->data; 422562306a36Sopenharmony_ci } 422662306a36Sopenharmony_ci 422762306a36Sopenharmony_ci ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, 422862306a36Sopenharmony_ci elems->vht_cap_elem, 422962306a36Sopenharmony_ci bss_vht_cap, link_sta); 423062306a36Sopenharmony_ci rcu_read_unlock(); 423162306a36Sopenharmony_ci } 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && 423462306a36Sopenharmony_ci elems->he_cap) { 423562306a36Sopenharmony_ci ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, 423662306a36Sopenharmony_ci elems->he_cap, 423762306a36Sopenharmony_ci elems->he_cap_len, 423862306a36Sopenharmony_ci elems->he_6ghz_capa, 423962306a36Sopenharmony_ci link_sta); 424062306a36Sopenharmony_ci 424162306a36Sopenharmony_ci bss_conf->he_support = link_sta->pub->he_cap.has_he; 424262306a36Sopenharmony_ci if (elems->rsnx && elems->rsnx_len && 424362306a36Sopenharmony_ci (elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) && 424462306a36Sopenharmony_ci wiphy_ext_feature_isset(local->hw.wiphy, 424562306a36Sopenharmony_ci NL80211_EXT_FEATURE_PROTECTED_TWT)) 424662306a36Sopenharmony_ci bss_conf->twt_protected = true; 424762306a36Sopenharmony_ci else 424862306a36Sopenharmony_ci bss_conf->twt_protected = false; 424962306a36Sopenharmony_ci 425062306a36Sopenharmony_ci *changed |= ieee80211_recalc_twt_req(sdata, sband, link, 425162306a36Sopenharmony_ci link_sta, elems); 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_ci if (elems->eht_operation && elems->eht_cap && 425462306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { 425562306a36Sopenharmony_ci ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband, 425662306a36Sopenharmony_ci elems->he_cap, 425762306a36Sopenharmony_ci elems->he_cap_len, 425862306a36Sopenharmony_ci elems->eht_cap, 425962306a36Sopenharmony_ci elems->eht_cap_len, 426062306a36Sopenharmony_ci link_sta); 426162306a36Sopenharmony_ci 426262306a36Sopenharmony_ci bss_conf->eht_support = link_sta->pub->eht_cap.has_eht; 426362306a36Sopenharmony_ci *changed |= BSS_CHANGED_EHT_PUNCTURING; 426462306a36Sopenharmony_ci } else { 426562306a36Sopenharmony_ci bss_conf->eht_support = false; 426662306a36Sopenharmony_ci } 426762306a36Sopenharmony_ci } else { 426862306a36Sopenharmony_ci bss_conf->he_support = false; 426962306a36Sopenharmony_ci bss_conf->twt_requester = false; 427062306a36Sopenharmony_ci bss_conf->twt_protected = false; 427162306a36Sopenharmony_ci bss_conf->eht_support = false; 427262306a36Sopenharmony_ci } 427362306a36Sopenharmony_ci 427462306a36Sopenharmony_ci bss_conf->twt_broadcast = 427562306a36Sopenharmony_ci ieee80211_twt_bcast_support(sdata, bss_conf, sband, link_sta); 427662306a36Sopenharmony_ci 427762306a36Sopenharmony_ci if (bss_conf->he_support) { 427862306a36Sopenharmony_ci bss_conf->he_bss_color.color = 427962306a36Sopenharmony_ci le32_get_bits(elems->he_operation->he_oper_params, 428062306a36Sopenharmony_ci IEEE80211_HE_OPERATION_BSS_COLOR_MASK); 428162306a36Sopenharmony_ci bss_conf->he_bss_color.partial = 428262306a36Sopenharmony_ci le32_get_bits(elems->he_operation->he_oper_params, 428362306a36Sopenharmony_ci IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR); 428462306a36Sopenharmony_ci bss_conf->he_bss_color.enabled = 428562306a36Sopenharmony_ci !le32_get_bits(elems->he_operation->he_oper_params, 428662306a36Sopenharmony_ci IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED); 428762306a36Sopenharmony_ci 428862306a36Sopenharmony_ci if (bss_conf->he_bss_color.enabled) 428962306a36Sopenharmony_ci *changed |= BSS_CHANGED_HE_BSS_COLOR; 429062306a36Sopenharmony_ci 429162306a36Sopenharmony_ci bss_conf->htc_trig_based_pkt_ext = 429262306a36Sopenharmony_ci le32_get_bits(elems->he_operation->he_oper_params, 429362306a36Sopenharmony_ci IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK); 429462306a36Sopenharmony_ci bss_conf->frame_time_rts_th = 429562306a36Sopenharmony_ci le32_get_bits(elems->he_operation->he_oper_params, 429662306a36Sopenharmony_ci IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); 429762306a36Sopenharmony_ci 429862306a36Sopenharmony_ci bss_conf->uora_exists = !!elems->uora_element; 429962306a36Sopenharmony_ci if (elems->uora_element) 430062306a36Sopenharmony_ci bss_conf->uora_ocw_range = elems->uora_element[0]; 430162306a36Sopenharmony_ci 430262306a36Sopenharmony_ci ieee80211_he_op_ie_to_bss_conf(&sdata->vif, elems->he_operation); 430362306a36Sopenharmony_ci ieee80211_he_spr_ie_to_bss_conf(&sdata->vif, elems->he_spr); 430462306a36Sopenharmony_ci /* TODO: OPEN: what happens if BSS color disable is set? */ 430562306a36Sopenharmony_ci } 430662306a36Sopenharmony_ci 430762306a36Sopenharmony_ci if (cbss->transmitted_bss) { 430862306a36Sopenharmony_ci bss_conf->nontransmitted = true; 430962306a36Sopenharmony_ci ether_addr_copy(bss_conf->transmitter_bssid, 431062306a36Sopenharmony_ci cbss->transmitted_bss->bssid); 431162306a36Sopenharmony_ci bss_conf->bssid_indicator = cbss->max_bssid_indicator; 431262306a36Sopenharmony_ci bss_conf->bssid_index = cbss->bssid_index; 431362306a36Sopenharmony_ci } 431462306a36Sopenharmony_ci 431562306a36Sopenharmony_ci /* 431662306a36Sopenharmony_ci * Some APs, e.g. Netgear WNDR3700, report invalid HT operation data 431762306a36Sopenharmony_ci * in their association response, so ignore that data for our own 431862306a36Sopenharmony_ci * configuration. If it changed since the last beacon, we'll get the 431962306a36Sopenharmony_ci * next beacon and update then. 432062306a36Sopenharmony_ci */ 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci /* 432362306a36Sopenharmony_ci * If an operating mode notification IE is present, override the 432462306a36Sopenharmony_ci * NSS calculation (that would be done in rate_control_rate_init()) 432562306a36Sopenharmony_ci * and use the # of streams from that element. 432662306a36Sopenharmony_ci */ 432762306a36Sopenharmony_ci if (elems->opmode_notif && 432862306a36Sopenharmony_ci !(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) { 432962306a36Sopenharmony_ci u8 nss; 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; 433262306a36Sopenharmony_ci nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; 433362306a36Sopenharmony_ci nss += 1; 433462306a36Sopenharmony_ci link_sta->pub->rx_nss = nss; 433562306a36Sopenharmony_ci } 433662306a36Sopenharmony_ci 433762306a36Sopenharmony_ci /* 433862306a36Sopenharmony_ci * Always handle WMM once after association regardless 433962306a36Sopenharmony_ci * of the first value the AP uses. Setting -1 here has 434062306a36Sopenharmony_ci * that effect because the AP values is an unsigned 434162306a36Sopenharmony_ci * 4-bit value. 434262306a36Sopenharmony_ci */ 434362306a36Sopenharmony_ci link->u.mgd.wmm_last_param_set = -1; 434462306a36Sopenharmony_ci link->u.mgd.mu_edca_last_param_set = -1; 434562306a36Sopenharmony_ci 434662306a36Sopenharmony_ci if (link->u.mgd.disable_wmm_tracking) { 434762306a36Sopenharmony_ci ieee80211_set_wmm_default(link, false, false); 434862306a36Sopenharmony_ci } else if (!ieee80211_sta_wmm_params(local, link, elems->wmm_param, 434962306a36Sopenharmony_ci elems->wmm_param_len, 435062306a36Sopenharmony_ci elems->mu_edca_param_set)) { 435162306a36Sopenharmony_ci /* still enable QoS since we might have HT/VHT */ 435262306a36Sopenharmony_ci ieee80211_set_wmm_default(link, false, true); 435362306a36Sopenharmony_ci /* disable WMM tracking in this case to disable 435462306a36Sopenharmony_ci * tracking WMM parameter changes in the beacon if 435562306a36Sopenharmony_ci * the parameters weren't actually valid. Doing so 435662306a36Sopenharmony_ci * avoids changing parameters very strangely when 435762306a36Sopenharmony_ci * the AP is going back and forth between valid and 435862306a36Sopenharmony_ci * invalid parameters. 435962306a36Sopenharmony_ci */ 436062306a36Sopenharmony_ci link->u.mgd.disable_wmm_tracking = true; 436162306a36Sopenharmony_ci } 436262306a36Sopenharmony_ci 436362306a36Sopenharmony_ci if (elems->max_idle_period_ie) { 436462306a36Sopenharmony_ci bss_conf->max_idle_period = 436562306a36Sopenharmony_ci le16_to_cpu(elems->max_idle_period_ie->max_idle_period); 436662306a36Sopenharmony_ci bss_conf->protected_keep_alive = 436762306a36Sopenharmony_ci !!(elems->max_idle_period_ie->idle_options & 436862306a36Sopenharmony_ci WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE); 436962306a36Sopenharmony_ci *changed |= BSS_CHANGED_KEEP_ALIVE; 437062306a36Sopenharmony_ci } else { 437162306a36Sopenharmony_ci bss_conf->max_idle_period = 0; 437262306a36Sopenharmony_ci bss_conf->protected_keep_alive = false; 437362306a36Sopenharmony_ci } 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci /* set assoc capability (AID was already set earlier), 437662306a36Sopenharmony_ci * ieee80211_set_associated() will tell the driver */ 437762306a36Sopenharmony_ci bss_conf->assoc_capability = capab_info; 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci ret = true; 438062306a36Sopenharmony_ciout: 438162306a36Sopenharmony_ci kfree(elems); 438262306a36Sopenharmony_ci kfree(bss_ies); 438362306a36Sopenharmony_ci return ret; 438462306a36Sopenharmony_ci} 438562306a36Sopenharmony_ci 438662306a36Sopenharmony_cistatic int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link, 438762306a36Sopenharmony_ci struct sta_info *sta, 438862306a36Sopenharmony_ci struct link_sta_info *link_sta, 438962306a36Sopenharmony_ci struct cfg80211_bss *cbss) 439062306a36Sopenharmony_ci{ 439162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 439262306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 439362306a36Sopenharmony_ci struct ieee80211_bss *bss = (void *)cbss->priv; 439462306a36Sopenharmony_ci u32 rates = 0, basic_rates = 0; 439562306a36Sopenharmony_ci bool have_higher_than_11mbit = false; 439662306a36Sopenharmony_ci int min_rate = INT_MAX, min_rate_index = -1; 439762306a36Sopenharmony_ci /* this is clearly wrong for MLO but we'll just remove it later */ 439862306a36Sopenharmony_ci int shift = ieee80211_vif_get_shift(&sdata->vif); 439962306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_ci memcpy(link_sta->addr, cbss->bssid, ETH_ALEN); 440262306a36Sopenharmony_ci memcpy(link_sta->pub->addr, cbss->bssid, ETH_ALEN); 440362306a36Sopenharmony_ci 440462306a36Sopenharmony_ci /* TODO: S1G Basic Rate Set is expressed elsewhere */ 440562306a36Sopenharmony_ci if (cbss->channel->band == NL80211_BAND_S1GHZ) { 440662306a36Sopenharmony_ci ieee80211_s1g_sta_rate_init(sta); 440762306a36Sopenharmony_ci return 0; 440862306a36Sopenharmony_ci } 440962306a36Sopenharmony_ci 441062306a36Sopenharmony_ci sband = local->hw.wiphy->bands[cbss->channel->band]; 441162306a36Sopenharmony_ci 441262306a36Sopenharmony_ci ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len, 441362306a36Sopenharmony_ci &rates, &basic_rates, &have_higher_than_11mbit, 441462306a36Sopenharmony_ci &min_rate, &min_rate_index, shift); 441562306a36Sopenharmony_ci 441662306a36Sopenharmony_ci /* 441762306a36Sopenharmony_ci * This used to be a workaround for basic rates missing 441862306a36Sopenharmony_ci * in the association response frame. Now that we no 441962306a36Sopenharmony_ci * longer use the basic rates from there, it probably 442062306a36Sopenharmony_ci * doesn't happen any more, but keep the workaround so 442162306a36Sopenharmony_ci * in case some *other* APs are buggy in different ways 442262306a36Sopenharmony_ci * we can connect -- with a warning. 442362306a36Sopenharmony_ci * Allow this workaround only in case the AP provided at least 442462306a36Sopenharmony_ci * one rate. 442562306a36Sopenharmony_ci */ 442662306a36Sopenharmony_ci if (min_rate_index < 0) { 442762306a36Sopenharmony_ci link_info(link, "No legacy rates in association response\n"); 442862306a36Sopenharmony_ci return -EINVAL; 442962306a36Sopenharmony_ci } else if (!basic_rates) { 443062306a36Sopenharmony_ci link_info(link, "No basic rates, using min rate instead\n"); 443162306a36Sopenharmony_ci basic_rates = BIT(min_rate_index); 443262306a36Sopenharmony_ci } 443362306a36Sopenharmony_ci 443462306a36Sopenharmony_ci if (rates) 443562306a36Sopenharmony_ci link_sta->pub->supp_rates[cbss->channel->band] = rates; 443662306a36Sopenharmony_ci else 443762306a36Sopenharmony_ci link_info(link, "No rates found, keeping mandatory only\n"); 443862306a36Sopenharmony_ci 443962306a36Sopenharmony_ci link->conf->basic_rates = basic_rates; 444062306a36Sopenharmony_ci 444162306a36Sopenharmony_ci /* cf. IEEE 802.11 9.2.12 */ 444262306a36Sopenharmony_ci link->operating_11g_mode = sband->band == NL80211_BAND_2GHZ && 444362306a36Sopenharmony_ci have_higher_than_11mbit; 444462306a36Sopenharmony_ci 444562306a36Sopenharmony_ci return 0; 444662306a36Sopenharmony_ci} 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_cistatic u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link, 444962306a36Sopenharmony_ci struct cfg80211_bss *cbss) 445062306a36Sopenharmony_ci{ 445162306a36Sopenharmony_ci struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; 445262306a36Sopenharmony_ci const struct element *ht_cap_elem, *vht_cap_elem; 445362306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 445462306a36Sopenharmony_ci const struct ieee80211_ht_cap *ht_cap; 445562306a36Sopenharmony_ci const struct ieee80211_vht_cap *vht_cap; 445662306a36Sopenharmony_ci const struct ieee80211_he_cap_elem *he_cap; 445762306a36Sopenharmony_ci const struct element *he_cap_elem; 445862306a36Sopenharmony_ci u16 mcs_80_map, mcs_160_map; 445962306a36Sopenharmony_ci int i, mcs_nss_size; 446062306a36Sopenharmony_ci bool support_160; 446162306a36Sopenharmony_ci u8 chains = 1; 446262306a36Sopenharmony_ci 446362306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) 446462306a36Sopenharmony_ci return chains; 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci ht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_CAPABILITY); 446762306a36Sopenharmony_ci if (ht_cap_elem && ht_cap_elem->datalen >= sizeof(*ht_cap)) { 446862306a36Sopenharmony_ci ht_cap = (void *)ht_cap_elem->data; 446962306a36Sopenharmony_ci chains = ieee80211_mcs_to_chains(&ht_cap->mcs); 447062306a36Sopenharmony_ci /* 447162306a36Sopenharmony_ci * TODO: use "Tx Maximum Number Spatial Streams Supported" and 447262306a36Sopenharmony_ci * "Tx Unequal Modulation Supported" fields. 447362306a36Sopenharmony_ci */ 447462306a36Sopenharmony_ci } 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) 447762306a36Sopenharmony_ci return chains; 447862306a36Sopenharmony_ci 447962306a36Sopenharmony_ci vht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY); 448062306a36Sopenharmony_ci if (vht_cap_elem && vht_cap_elem->datalen >= sizeof(*vht_cap)) { 448162306a36Sopenharmony_ci u8 nss; 448262306a36Sopenharmony_ci u16 tx_mcs_map; 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_ci vht_cap = (void *)vht_cap_elem->data; 448562306a36Sopenharmony_ci tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); 448662306a36Sopenharmony_ci for (nss = 8; nss > 0; nss--) { 448762306a36Sopenharmony_ci if (((tx_mcs_map >> (2 * (nss - 1))) & 3) != 448862306a36Sopenharmony_ci IEEE80211_VHT_MCS_NOT_SUPPORTED) 448962306a36Sopenharmony_ci break; 449062306a36Sopenharmony_ci } 449162306a36Sopenharmony_ci /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */ 449262306a36Sopenharmony_ci chains = max(chains, nss); 449362306a36Sopenharmony_ci } 449462306a36Sopenharmony_ci 449562306a36Sopenharmony_ci if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) 449662306a36Sopenharmony_ci return chains; 449762306a36Sopenharmony_ci 449862306a36Sopenharmony_ci ies = rcu_dereference(cbss->ies); 449962306a36Sopenharmony_ci he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, 450062306a36Sopenharmony_ci ies->data, ies->len); 450162306a36Sopenharmony_ci 450262306a36Sopenharmony_ci if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap)) 450362306a36Sopenharmony_ci return chains; 450462306a36Sopenharmony_ci 450562306a36Sopenharmony_ci /* skip one byte ext_tag_id */ 450662306a36Sopenharmony_ci he_cap = (void *)(he_cap_elem->data + 1); 450762306a36Sopenharmony_ci mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); 450862306a36Sopenharmony_ci 450962306a36Sopenharmony_ci /* invalid HE IE */ 451062306a36Sopenharmony_ci if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap)) 451162306a36Sopenharmony_ci return chains; 451262306a36Sopenharmony_ci 451362306a36Sopenharmony_ci /* mcs_nss is right after he_cap info */ 451462306a36Sopenharmony_ci he_mcs_nss_supp = (void *)(he_cap + 1); 451562306a36Sopenharmony_ci 451662306a36Sopenharmony_ci mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci for (i = 7; i >= 0; i--) { 451962306a36Sopenharmony_ci u8 mcs_80 = mcs_80_map >> (2 * i) & 3; 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 452262306a36Sopenharmony_ci chains = max_t(u8, chains, i + 1); 452362306a36Sopenharmony_ci break; 452462306a36Sopenharmony_ci } 452562306a36Sopenharmony_ci } 452662306a36Sopenharmony_ci 452762306a36Sopenharmony_ci support_160 = he_cap->phy_cap_info[0] & 452862306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 452962306a36Sopenharmony_ci 453062306a36Sopenharmony_ci if (!support_160) 453162306a36Sopenharmony_ci return chains; 453262306a36Sopenharmony_ci 453362306a36Sopenharmony_ci mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160); 453462306a36Sopenharmony_ci for (i = 7; i >= 0; i--) { 453562306a36Sopenharmony_ci u8 mcs_160 = mcs_160_map >> (2 * i) & 3; 453662306a36Sopenharmony_ci 453762306a36Sopenharmony_ci if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 453862306a36Sopenharmony_ci chains = max_t(u8, chains, i + 1); 453962306a36Sopenharmony_ci break; 454062306a36Sopenharmony_ci } 454162306a36Sopenharmony_ci } 454262306a36Sopenharmony_ci 454362306a36Sopenharmony_ci return chains; 454462306a36Sopenharmony_ci} 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_cistatic bool 454762306a36Sopenharmony_ciieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata, 454862306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies, 454962306a36Sopenharmony_ci const struct ieee80211_he_operation *he_op) 455062306a36Sopenharmony_ci{ 455162306a36Sopenharmony_ci const struct element *he_cap_elem; 455262306a36Sopenharmony_ci const struct ieee80211_he_cap_elem *he_cap; 455362306a36Sopenharmony_ci struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp; 455462306a36Sopenharmony_ci u16 mcs_80_map_tx, mcs_80_map_rx; 455562306a36Sopenharmony_ci u16 ap_min_req_set; 455662306a36Sopenharmony_ci int mcs_nss_size; 455762306a36Sopenharmony_ci int nss; 455862306a36Sopenharmony_ci 455962306a36Sopenharmony_ci he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, 456062306a36Sopenharmony_ci ies->data, ies->len); 456162306a36Sopenharmony_ci 456262306a36Sopenharmony_ci if (!he_cap_elem) 456362306a36Sopenharmony_ci return false; 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci /* invalid HE IE */ 456662306a36Sopenharmony_ci if (he_cap_elem->datalen < 1 + sizeof(*he_cap)) { 456762306a36Sopenharmony_ci sdata_info(sdata, 456862306a36Sopenharmony_ci "Invalid HE elem, Disable HE\n"); 456962306a36Sopenharmony_ci return false; 457062306a36Sopenharmony_ci } 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci /* skip one byte ext_tag_id */ 457362306a36Sopenharmony_ci he_cap = (void *)(he_cap_elem->data + 1); 457462306a36Sopenharmony_ci mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap); 457562306a36Sopenharmony_ci 457662306a36Sopenharmony_ci /* invalid HE IE */ 457762306a36Sopenharmony_ci if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) { 457862306a36Sopenharmony_ci sdata_info(sdata, 457962306a36Sopenharmony_ci "Invalid HE elem with nss size, Disable HE\n"); 458062306a36Sopenharmony_ci return false; 458162306a36Sopenharmony_ci } 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_ci /* mcs_nss is right after he_cap info */ 458462306a36Sopenharmony_ci he_mcs_nss_supp = (void *)(he_cap + 1); 458562306a36Sopenharmony_ci 458662306a36Sopenharmony_ci mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80); 458762306a36Sopenharmony_ci mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80); 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_ci /* P802.11-REVme/D0.3 459062306a36Sopenharmony_ci * 27.1.1 Introduction to the HE PHY 459162306a36Sopenharmony_ci * ... 459262306a36Sopenharmony_ci * An HE STA shall support the following features: 459362306a36Sopenharmony_ci * ... 459462306a36Sopenharmony_ci * Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all 459562306a36Sopenharmony_ci * supported channel widths for HE SU PPDUs 459662306a36Sopenharmony_ci */ 459762306a36Sopenharmony_ci if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED || 459862306a36Sopenharmony_ci (mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) { 459962306a36Sopenharmony_ci sdata_info(sdata, 460062306a36Sopenharmony_ci "Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n", 460162306a36Sopenharmony_ci mcs_80_map_tx, mcs_80_map_rx); 460262306a36Sopenharmony_ci return false; 460362306a36Sopenharmony_ci } 460462306a36Sopenharmony_ci 460562306a36Sopenharmony_ci if (!he_op) 460662306a36Sopenharmony_ci return true; 460762306a36Sopenharmony_ci 460862306a36Sopenharmony_ci ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); 460962306a36Sopenharmony_ci 461062306a36Sopenharmony_ci /* 461162306a36Sopenharmony_ci * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all 461262306a36Sopenharmony_ci * zeroes, which is nonsense, and completely inconsistent with itself 461362306a36Sopenharmony_ci * (it doesn't have 8 streams). Accept the settings in this case anyway. 461462306a36Sopenharmony_ci */ 461562306a36Sopenharmony_ci if (!ap_min_req_set) 461662306a36Sopenharmony_ci return true; 461762306a36Sopenharmony_ci 461862306a36Sopenharmony_ci /* make sure the AP is consistent with itself 461962306a36Sopenharmony_ci * 462062306a36Sopenharmony_ci * P802.11-REVme/D0.3 462162306a36Sopenharmony_ci * 26.17.1 Basic HE BSS operation 462262306a36Sopenharmony_ci * 462362306a36Sopenharmony_ci * A STA that is operating in an HE BSS shall be able to receive and 462462306a36Sopenharmony_ci * transmit at each of the <HE-MCS, NSS> tuple values indicated by the 462562306a36Sopenharmony_ci * Basic HE-MCS And NSS Set field of the HE Operation parameter of the 462662306a36Sopenharmony_ci * MLME-START.request primitive and shall be able to receive at each of 462762306a36Sopenharmony_ci * the <HE-MCS, NSS> tuple values indicated by the Supported HE-MCS and 462862306a36Sopenharmony_ci * NSS Set field in the HE Capabilities parameter of the MLMESTART.request 462962306a36Sopenharmony_ci * primitive 463062306a36Sopenharmony_ci */ 463162306a36Sopenharmony_ci for (nss = 8; nss > 0; nss--) { 463262306a36Sopenharmony_ci u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; 463362306a36Sopenharmony_ci u8 ap_rx_val; 463462306a36Sopenharmony_ci u8 ap_tx_val; 463562306a36Sopenharmony_ci 463662306a36Sopenharmony_ci if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED) 463762306a36Sopenharmony_ci continue; 463862306a36Sopenharmony_ci 463962306a36Sopenharmony_ci ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3; 464062306a36Sopenharmony_ci ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3; 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_ci if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || 464362306a36Sopenharmony_ci ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || 464462306a36Sopenharmony_ci ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) { 464562306a36Sopenharmony_ci sdata_info(sdata, 464662306a36Sopenharmony_ci "Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n", 464762306a36Sopenharmony_ci nss, ap_rx_val, ap_rx_val, ap_op_val); 464862306a36Sopenharmony_ci return false; 464962306a36Sopenharmony_ci } 465062306a36Sopenharmony_ci } 465162306a36Sopenharmony_ci 465262306a36Sopenharmony_ci return true; 465362306a36Sopenharmony_ci} 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_cistatic bool 465662306a36Sopenharmony_ciieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata, 465762306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 465862306a36Sopenharmony_ci const struct ieee80211_he_operation *he_op) 465962306a36Sopenharmony_ci{ 466062306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *sta_he_cap = 466162306a36Sopenharmony_ci ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 466262306a36Sopenharmony_ci u16 ap_min_req_set; 466362306a36Sopenharmony_ci int i; 466462306a36Sopenharmony_ci 466562306a36Sopenharmony_ci if (!sta_he_cap || !he_op) 466662306a36Sopenharmony_ci return false; 466762306a36Sopenharmony_ci 466862306a36Sopenharmony_ci ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set); 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci /* 467162306a36Sopenharmony_ci * Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all 467262306a36Sopenharmony_ci * zeroes, which is nonsense, and completely inconsistent with itself 467362306a36Sopenharmony_ci * (it doesn't have 8 streams). Accept the settings in this case anyway. 467462306a36Sopenharmony_ci */ 467562306a36Sopenharmony_ci if (!ap_min_req_set) 467662306a36Sopenharmony_ci return true; 467762306a36Sopenharmony_ci 467862306a36Sopenharmony_ci /* Need to go over for 80MHz, 160MHz and for 80+80 */ 467962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 468062306a36Sopenharmony_ci const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp = 468162306a36Sopenharmony_ci &sta_he_cap->he_mcs_nss_supp; 468262306a36Sopenharmony_ci u16 sta_mcs_map_rx = 468362306a36Sopenharmony_ci le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]); 468462306a36Sopenharmony_ci u16 sta_mcs_map_tx = 468562306a36Sopenharmony_ci le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]); 468662306a36Sopenharmony_ci u8 nss; 468762306a36Sopenharmony_ci bool verified = true; 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci /* 469062306a36Sopenharmony_ci * For each band there is a maximum of 8 spatial streams 469162306a36Sopenharmony_ci * possible. Each of the sta_mcs_map_* is a 16-bit struct built 469262306a36Sopenharmony_ci * of 2 bits per NSS (1-8), with the values defined in enum 469362306a36Sopenharmony_ci * ieee80211_he_mcs_support. Need to make sure STA TX and RX 469462306a36Sopenharmony_ci * capabilities aren't less than the AP's minimum requirements 469562306a36Sopenharmony_ci * for this HE BSS per SS. 469662306a36Sopenharmony_ci * It is enough to find one such band that meets the reqs. 469762306a36Sopenharmony_ci */ 469862306a36Sopenharmony_ci for (nss = 8; nss > 0; nss--) { 469962306a36Sopenharmony_ci u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3; 470062306a36Sopenharmony_ci u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3; 470162306a36Sopenharmony_ci u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3; 470262306a36Sopenharmony_ci 470362306a36Sopenharmony_ci if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED) 470462306a36Sopenharmony_ci continue; 470562306a36Sopenharmony_ci 470662306a36Sopenharmony_ci /* 470762306a36Sopenharmony_ci * Make sure the HE AP doesn't require MCSs that aren't 470862306a36Sopenharmony_ci * supported by the client as required by spec 470962306a36Sopenharmony_ci * 471062306a36Sopenharmony_ci * P802.11-REVme/D0.3 471162306a36Sopenharmony_ci * 26.17.1 Basic HE BSS operation 471262306a36Sopenharmony_ci * 471362306a36Sopenharmony_ci * An HE STA shall not attempt to join * (MLME-JOIN.request primitive) 471462306a36Sopenharmony_ci * a BSS, unless it supports (i.e., is able to both transmit and 471562306a36Sopenharmony_ci * receive using) all of the <HE-MCS, NSS> tuples in the basic 471662306a36Sopenharmony_ci * HE-MCS and NSS set. 471762306a36Sopenharmony_ci */ 471862306a36Sopenharmony_ci if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || 471962306a36Sopenharmony_ci sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED || 472062306a36Sopenharmony_ci (ap_val > sta_rx_val) || (ap_val > sta_tx_val)) { 472162306a36Sopenharmony_ci verified = false; 472262306a36Sopenharmony_ci break; 472362306a36Sopenharmony_ci } 472462306a36Sopenharmony_ci } 472562306a36Sopenharmony_ci 472662306a36Sopenharmony_ci if (verified) 472762306a36Sopenharmony_ci return true; 472862306a36Sopenharmony_ci } 472962306a36Sopenharmony_ci 473062306a36Sopenharmony_ci /* If here, STA doesn't meet AP's HE min requirements */ 473162306a36Sopenharmony_ci return false; 473262306a36Sopenharmony_ci} 473362306a36Sopenharmony_ci 473462306a36Sopenharmony_cistatic u8 473562306a36Sopenharmony_ciieee80211_get_eht_cap_mcs_nss(const struct ieee80211_sta_he_cap *sta_he_cap, 473662306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *sta_eht_cap, 473762306a36Sopenharmony_ci unsigned int idx, int bw) 473862306a36Sopenharmony_ci{ 473962306a36Sopenharmony_ci u8 he_phy_cap0 = sta_he_cap->he_cap_elem.phy_cap_info[0]; 474062306a36Sopenharmony_ci u8 eht_phy_cap0 = sta_eht_cap->eht_cap_elem.phy_cap_info[0]; 474162306a36Sopenharmony_ci 474262306a36Sopenharmony_ci /* handle us being a 20 MHz-only EHT STA - with four values 474362306a36Sopenharmony_ci * for MCS 0-7, 8-9, 10-11, 12-13. 474462306a36Sopenharmony_ci */ 474562306a36Sopenharmony_ci if (!(he_phy_cap0 & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) 474662306a36Sopenharmony_ci return sta_eht_cap->eht_mcs_nss_supp.only_20mhz.rx_tx_max_nss[idx]; 474762306a36Sopenharmony_ci 474862306a36Sopenharmony_ci /* the others have MCS 0-9 together, rather than separately from 0-7 */ 474962306a36Sopenharmony_ci if (idx > 0) 475062306a36Sopenharmony_ci idx--; 475162306a36Sopenharmony_ci 475262306a36Sopenharmony_ci switch (bw) { 475362306a36Sopenharmony_ci case 0: 475462306a36Sopenharmony_ci return sta_eht_cap->eht_mcs_nss_supp.bw._80.rx_tx_max_nss[idx]; 475562306a36Sopenharmony_ci case 1: 475662306a36Sopenharmony_ci if (!(he_phy_cap0 & 475762306a36Sopenharmony_ci (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | 475862306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G))) 475962306a36Sopenharmony_ci return 0xff; /* pass check */ 476062306a36Sopenharmony_ci return sta_eht_cap->eht_mcs_nss_supp.bw._160.rx_tx_max_nss[idx]; 476162306a36Sopenharmony_ci case 2: 476262306a36Sopenharmony_ci if (!(eht_phy_cap0 & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ)) 476362306a36Sopenharmony_ci return 0xff; /* pass check */ 476462306a36Sopenharmony_ci return sta_eht_cap->eht_mcs_nss_supp.bw._320.rx_tx_max_nss[idx]; 476562306a36Sopenharmony_ci } 476662306a36Sopenharmony_ci 476762306a36Sopenharmony_ci WARN_ON(1); 476862306a36Sopenharmony_ci return 0; 476962306a36Sopenharmony_ci} 477062306a36Sopenharmony_ci 477162306a36Sopenharmony_cistatic bool 477262306a36Sopenharmony_ciieee80211_verify_sta_eht_mcs_support(struct ieee80211_sub_if_data *sdata, 477362306a36Sopenharmony_ci struct ieee80211_supported_band *sband, 477462306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_op) 477562306a36Sopenharmony_ci{ 477662306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *sta_he_cap = 477762306a36Sopenharmony_ci ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 477862306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *sta_eht_cap = 477962306a36Sopenharmony_ci ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); 478062306a36Sopenharmony_ci const struct ieee80211_eht_mcs_nss_supp_20mhz_only *req; 478162306a36Sopenharmony_ci unsigned int i; 478262306a36Sopenharmony_ci 478362306a36Sopenharmony_ci if (!sta_he_cap || !sta_eht_cap || !eht_op) 478462306a36Sopenharmony_ci return false; 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_ci req = &eht_op->basic_mcs_nss; 478762306a36Sopenharmony_ci 478862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(req->rx_tx_max_nss); i++) { 478962306a36Sopenharmony_ci u8 req_rx_nss, req_tx_nss; 479062306a36Sopenharmony_ci unsigned int bw; 479162306a36Sopenharmony_ci 479262306a36Sopenharmony_ci req_rx_nss = u8_get_bits(req->rx_tx_max_nss[i], 479362306a36Sopenharmony_ci IEEE80211_EHT_MCS_NSS_RX); 479462306a36Sopenharmony_ci req_tx_nss = u8_get_bits(req->rx_tx_max_nss[i], 479562306a36Sopenharmony_ci IEEE80211_EHT_MCS_NSS_TX); 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci for (bw = 0; bw < 3; bw++) { 479862306a36Sopenharmony_ci u8 have, have_rx_nss, have_tx_nss; 479962306a36Sopenharmony_ci 480062306a36Sopenharmony_ci have = ieee80211_get_eht_cap_mcs_nss(sta_he_cap, 480162306a36Sopenharmony_ci sta_eht_cap, 480262306a36Sopenharmony_ci i, bw); 480362306a36Sopenharmony_ci have_rx_nss = u8_get_bits(have, 480462306a36Sopenharmony_ci IEEE80211_EHT_MCS_NSS_RX); 480562306a36Sopenharmony_ci have_tx_nss = u8_get_bits(have, 480662306a36Sopenharmony_ci IEEE80211_EHT_MCS_NSS_TX); 480762306a36Sopenharmony_ci 480862306a36Sopenharmony_ci if (req_rx_nss > have_rx_nss || 480962306a36Sopenharmony_ci req_tx_nss > have_tx_nss) 481062306a36Sopenharmony_ci return false; 481162306a36Sopenharmony_ci } 481262306a36Sopenharmony_ci } 481362306a36Sopenharmony_ci 481462306a36Sopenharmony_ci return true; 481562306a36Sopenharmony_ci} 481662306a36Sopenharmony_ci 481762306a36Sopenharmony_cistatic int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, 481862306a36Sopenharmony_ci struct ieee80211_link_data *link, 481962306a36Sopenharmony_ci struct cfg80211_bss *cbss, 482062306a36Sopenharmony_ci ieee80211_conn_flags_t *conn_flags) 482162306a36Sopenharmony_ci{ 482262306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 482362306a36Sopenharmony_ci const struct ieee80211_ht_cap *ht_cap = NULL; 482462306a36Sopenharmony_ci const struct ieee80211_ht_operation *ht_oper = NULL; 482562306a36Sopenharmony_ci const struct ieee80211_vht_operation *vht_oper = NULL; 482662306a36Sopenharmony_ci const struct ieee80211_he_operation *he_oper = NULL; 482762306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper = NULL; 482862306a36Sopenharmony_ci const struct ieee80211_s1g_oper_ie *s1g_oper = NULL; 482962306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 483062306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 483162306a36Sopenharmony_ci bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; 483262306a36Sopenharmony_ci bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; 483362306a36Sopenharmony_ci struct ieee80211_bss *bss = (void *)cbss->priv; 483462306a36Sopenharmony_ci struct ieee80211_elems_parse_params parse_params = { 483562306a36Sopenharmony_ci .link_id = -1, 483662306a36Sopenharmony_ci .from_ap = true, 483762306a36Sopenharmony_ci }; 483862306a36Sopenharmony_ci struct ieee802_11_elems *elems; 483962306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 484062306a36Sopenharmony_ci int ret; 484162306a36Sopenharmony_ci u32 i; 484262306a36Sopenharmony_ci bool have_80mhz; 484362306a36Sopenharmony_ci 484462306a36Sopenharmony_ci rcu_read_lock(); 484562306a36Sopenharmony_ci 484662306a36Sopenharmony_ci ies = rcu_dereference(cbss->ies); 484762306a36Sopenharmony_ci parse_params.start = ies->data; 484862306a36Sopenharmony_ci parse_params.len = ies->len; 484962306a36Sopenharmony_ci elems = ieee802_11_parse_elems_full(&parse_params); 485062306a36Sopenharmony_ci if (!elems) { 485162306a36Sopenharmony_ci rcu_read_unlock(); 485262306a36Sopenharmony_ci return -ENOMEM; 485362306a36Sopenharmony_ci } 485462306a36Sopenharmony_ci 485562306a36Sopenharmony_ci sband = local->hw.wiphy->bands[cbss->channel->band]; 485662306a36Sopenharmony_ci 485762306a36Sopenharmony_ci *conn_flags &= ~(IEEE80211_CONN_DISABLE_40MHZ | 485862306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_80P80MHZ | 485962306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_160MHZ); 486062306a36Sopenharmony_ci 486162306a36Sopenharmony_ci /* disable HT/VHT/HE if we don't support them */ 486262306a36Sopenharmony_ci if (!sband->ht_cap.ht_supported && !is_6ghz) { 486362306a36Sopenharmony_ci mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n"); 486462306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HT; 486562306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_VHT; 486662306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HE; 486762306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_EHT; 486862306a36Sopenharmony_ci } 486962306a36Sopenharmony_ci 487062306a36Sopenharmony_ci if (!sband->vht_cap.vht_supported && is_5ghz) { 487162306a36Sopenharmony_ci mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n"); 487262306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_VHT; 487362306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HE; 487462306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_EHT; 487562306a36Sopenharmony_ci } 487662306a36Sopenharmony_ci 487762306a36Sopenharmony_ci if (!ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif)) { 487862306a36Sopenharmony_ci mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n"); 487962306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HE; 488062306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_EHT; 488162306a36Sopenharmony_ci } 488262306a36Sopenharmony_ci 488362306a36Sopenharmony_ci if (!ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif)) { 488462306a36Sopenharmony_ci mlme_dbg(sdata, "EHT not supported, disabling EHT\n"); 488562306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_EHT; 488662306a36Sopenharmony_ci } 488762306a36Sopenharmony_ci 488862306a36Sopenharmony_ci if (!(*conn_flags & IEEE80211_CONN_DISABLE_HT) && !is_6ghz) { 488962306a36Sopenharmony_ci ht_oper = elems->ht_operation; 489062306a36Sopenharmony_ci ht_cap = elems->ht_cap_elem; 489162306a36Sopenharmony_ci 489262306a36Sopenharmony_ci if (!ht_cap) { 489362306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HT; 489462306a36Sopenharmony_ci ht_oper = NULL; 489562306a36Sopenharmony_ci } 489662306a36Sopenharmony_ci } 489762306a36Sopenharmony_ci 489862306a36Sopenharmony_ci if (!(*conn_flags & IEEE80211_CONN_DISABLE_VHT) && !is_6ghz) { 489962306a36Sopenharmony_ci vht_oper = elems->vht_operation; 490062306a36Sopenharmony_ci if (vht_oper && !ht_oper) { 490162306a36Sopenharmony_ci vht_oper = NULL; 490262306a36Sopenharmony_ci sdata_info(sdata, 490362306a36Sopenharmony_ci "AP advertised VHT without HT, disabling HT/VHT/HE\n"); 490462306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HT; 490562306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_VHT; 490662306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HE; 490762306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_EHT; 490862306a36Sopenharmony_ci } 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci if (!elems->vht_cap_elem) { 491162306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_VHT; 491262306a36Sopenharmony_ci vht_oper = NULL; 491362306a36Sopenharmony_ci } 491462306a36Sopenharmony_ci } 491562306a36Sopenharmony_ci 491662306a36Sopenharmony_ci if (!(*conn_flags & IEEE80211_CONN_DISABLE_HE)) { 491762306a36Sopenharmony_ci he_oper = elems->he_operation; 491862306a36Sopenharmony_ci 491962306a36Sopenharmony_ci if (link && is_6ghz) { 492062306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf; 492162306a36Sopenharmony_ci u8 j = 0; 492262306a36Sopenharmony_ci 492362306a36Sopenharmony_ci bss_conf = link->conf; 492462306a36Sopenharmony_ci 492562306a36Sopenharmony_ci if (elems->pwr_constr_elem) 492662306a36Sopenharmony_ci bss_conf->pwr_reduction = *elems->pwr_constr_elem; 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) != 492962306a36Sopenharmony_ci ARRAY_SIZE(elems->tx_pwr_env)); 493062306a36Sopenharmony_ci 493162306a36Sopenharmony_ci for (i = 0; i < elems->tx_pwr_env_num; i++) { 493262306a36Sopenharmony_ci if (elems->tx_pwr_env_len[i] > 493362306a36Sopenharmony_ci sizeof(bss_conf->tx_pwr_env[j])) 493462306a36Sopenharmony_ci continue; 493562306a36Sopenharmony_ci 493662306a36Sopenharmony_ci bss_conf->tx_pwr_env_num++; 493762306a36Sopenharmony_ci memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i], 493862306a36Sopenharmony_ci elems->tx_pwr_env_len[i]); 493962306a36Sopenharmony_ci j++; 494062306a36Sopenharmony_ci } 494162306a36Sopenharmony_ci } 494262306a36Sopenharmony_ci 494362306a36Sopenharmony_ci if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) || 494462306a36Sopenharmony_ci !ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper)) 494562306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_HE | 494662306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 494762306a36Sopenharmony_ci } 494862306a36Sopenharmony_ci 494962306a36Sopenharmony_ci /* 495062306a36Sopenharmony_ci * EHT requires HE to be supported as well. Specifically for 6 GHz 495162306a36Sopenharmony_ci * channels, the operation channel information can only be deduced from 495262306a36Sopenharmony_ci * both the 6 GHz operation information (from the HE operation IE) and 495362306a36Sopenharmony_ci * EHT operation. 495462306a36Sopenharmony_ci */ 495562306a36Sopenharmony_ci if (!(*conn_flags & 495662306a36Sopenharmony_ci (IEEE80211_CONN_DISABLE_HE | 495762306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT)) && 495862306a36Sopenharmony_ci he_oper) { 495962306a36Sopenharmony_ci const struct cfg80211_bss_ies *cbss_ies; 496062306a36Sopenharmony_ci const struct element *eht_ml_elem; 496162306a36Sopenharmony_ci const u8 *eht_oper_ie; 496262306a36Sopenharmony_ci 496362306a36Sopenharmony_ci cbss_ies = rcu_dereference(cbss->ies); 496462306a36Sopenharmony_ci eht_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_EHT_OPERATION, 496562306a36Sopenharmony_ci cbss_ies->data, cbss_ies->len); 496662306a36Sopenharmony_ci if (eht_oper_ie && eht_oper_ie[1] >= 496762306a36Sopenharmony_ci 1 + sizeof(struct ieee80211_eht_operation)) 496862306a36Sopenharmony_ci eht_oper = (void *)(eht_oper_ie + 3); 496962306a36Sopenharmony_ci else 497062306a36Sopenharmony_ci eht_oper = NULL; 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_ci if (!ieee80211_verify_sta_eht_mcs_support(sdata, sband, eht_oper)) 497362306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_EHT; 497462306a36Sopenharmony_ci 497562306a36Sopenharmony_ci eht_ml_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_MULTI_LINK, 497662306a36Sopenharmony_ci cbss_ies->data, cbss_ies->len); 497762306a36Sopenharmony_ci 497862306a36Sopenharmony_ci /* data + 1 / datalen - 1 since it's an extended element */ 497962306a36Sopenharmony_ci if (!(*conn_flags & IEEE80211_CONN_DISABLE_EHT) && 498062306a36Sopenharmony_ci eht_ml_elem && 498162306a36Sopenharmony_ci ieee80211_mle_type_ok(eht_ml_elem->data + 1, 498262306a36Sopenharmony_ci IEEE80211_ML_CONTROL_TYPE_BASIC, 498362306a36Sopenharmony_ci eht_ml_elem->datalen - 1)) { 498462306a36Sopenharmony_ci sdata->vif.cfg.eml_cap = 498562306a36Sopenharmony_ci ieee80211_mle_get_eml_cap(eht_ml_elem->data + 1); 498662306a36Sopenharmony_ci sdata->vif.cfg.eml_med_sync_delay = 498762306a36Sopenharmony_ci ieee80211_mle_get_eml_med_sync_delay(eht_ml_elem->data + 1); 498862306a36Sopenharmony_ci } 498962306a36Sopenharmony_ci } 499062306a36Sopenharmony_ci 499162306a36Sopenharmony_ci /* Allow VHT if at least one channel on the sband supports 80 MHz */ 499262306a36Sopenharmony_ci have_80mhz = false; 499362306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 499462306a36Sopenharmony_ci if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 499562306a36Sopenharmony_ci IEEE80211_CHAN_NO_80MHZ)) 499662306a36Sopenharmony_ci continue; 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci have_80mhz = true; 499962306a36Sopenharmony_ci break; 500062306a36Sopenharmony_ci } 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci if (!have_80mhz) { 500362306a36Sopenharmony_ci sdata_info(sdata, "80 MHz not supported, disabling VHT\n"); 500462306a36Sopenharmony_ci *conn_flags |= IEEE80211_CONN_DISABLE_VHT; 500562306a36Sopenharmony_ci } 500662306a36Sopenharmony_ci 500762306a36Sopenharmony_ci if (sband->band == NL80211_BAND_S1GHZ) { 500862306a36Sopenharmony_ci s1g_oper = elems->s1g_oper; 500962306a36Sopenharmony_ci if (!s1g_oper) 501062306a36Sopenharmony_ci sdata_info(sdata, 501162306a36Sopenharmony_ci "AP missing S1G operation element?\n"); 501262306a36Sopenharmony_ci } 501362306a36Sopenharmony_ci 501462306a36Sopenharmony_ci *conn_flags |= 501562306a36Sopenharmony_ci ieee80211_determine_chantype(sdata, link, *conn_flags, 501662306a36Sopenharmony_ci sband, 501762306a36Sopenharmony_ci cbss->channel, 501862306a36Sopenharmony_ci bss->vht_cap_info, 501962306a36Sopenharmony_ci ht_oper, vht_oper, 502062306a36Sopenharmony_ci he_oper, eht_oper, 502162306a36Sopenharmony_ci s1g_oper, 502262306a36Sopenharmony_ci &chandef, false); 502362306a36Sopenharmony_ci 502462306a36Sopenharmony_ci if (link) 502562306a36Sopenharmony_ci link->needed_rx_chains = 502662306a36Sopenharmony_ci min(ieee80211_max_rx_chains(link, cbss), 502762306a36Sopenharmony_ci local->rx_chains); 502862306a36Sopenharmony_ci 502962306a36Sopenharmony_ci rcu_read_unlock(); 503062306a36Sopenharmony_ci /* the element data was RCU protected so no longer valid anyway */ 503162306a36Sopenharmony_ci kfree(elems); 503262306a36Sopenharmony_ci elems = NULL; 503362306a36Sopenharmony_ci 503462306a36Sopenharmony_ci if (*conn_flags & IEEE80211_CONN_DISABLE_HE && is_6ghz) { 503562306a36Sopenharmony_ci sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection"); 503662306a36Sopenharmony_ci return -EINVAL; 503762306a36Sopenharmony_ci } 503862306a36Sopenharmony_ci 503962306a36Sopenharmony_ci if (!link) 504062306a36Sopenharmony_ci return 0; 504162306a36Sopenharmony_ci 504262306a36Sopenharmony_ci /* will change later if needed */ 504362306a36Sopenharmony_ci link->smps_mode = IEEE80211_SMPS_OFF; 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci mutex_lock(&local->mtx); 504662306a36Sopenharmony_ci /* 504762306a36Sopenharmony_ci * If this fails (possibly due to channel context sharing 504862306a36Sopenharmony_ci * on incompatible channels, e.g. 80+80 and 160 sharing the 504962306a36Sopenharmony_ci * same control channel) try to use a smaller bandwidth. 505062306a36Sopenharmony_ci */ 505162306a36Sopenharmony_ci ret = ieee80211_link_use_channel(link, &chandef, 505262306a36Sopenharmony_ci IEEE80211_CHANCTX_SHARED); 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci /* don't downgrade for 5 and 10 MHz channels, though. */ 505562306a36Sopenharmony_ci if (chandef.width == NL80211_CHAN_WIDTH_5 || 505662306a36Sopenharmony_ci chandef.width == NL80211_CHAN_WIDTH_10) 505762306a36Sopenharmony_ci goto out; 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { 506062306a36Sopenharmony_ci *conn_flags |= 506162306a36Sopenharmony_ci ieee80211_chandef_downgrade(&chandef); 506262306a36Sopenharmony_ci ret = ieee80211_link_use_channel(link, &chandef, 506362306a36Sopenharmony_ci IEEE80211_CHANCTX_SHARED); 506462306a36Sopenharmony_ci } 506562306a36Sopenharmony_ci out: 506662306a36Sopenharmony_ci mutex_unlock(&local->mtx); 506762306a36Sopenharmony_ci return ret; 506862306a36Sopenharmony_ci} 506962306a36Sopenharmony_ci 507062306a36Sopenharmony_cistatic bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies, 507162306a36Sopenharmony_ci u8 *dtim_count, u8 *dtim_period) 507262306a36Sopenharmony_ci{ 507362306a36Sopenharmony_ci const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len); 507462306a36Sopenharmony_ci const u8 *idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, ies->data, 507562306a36Sopenharmony_ci ies->len); 507662306a36Sopenharmony_ci const struct ieee80211_tim_ie *tim = NULL; 507762306a36Sopenharmony_ci const struct ieee80211_bssid_index *idx; 507862306a36Sopenharmony_ci bool valid = tim_ie && tim_ie[1] >= 2; 507962306a36Sopenharmony_ci 508062306a36Sopenharmony_ci if (valid) 508162306a36Sopenharmony_ci tim = (void *)(tim_ie + 2); 508262306a36Sopenharmony_ci 508362306a36Sopenharmony_ci if (dtim_count) 508462306a36Sopenharmony_ci *dtim_count = valid ? tim->dtim_count : 0; 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_ci if (dtim_period) 508762306a36Sopenharmony_ci *dtim_period = valid ? tim->dtim_period : 0; 508862306a36Sopenharmony_ci 508962306a36Sopenharmony_ci /* Check if value is overridden by non-transmitted profile */ 509062306a36Sopenharmony_ci if (!idx_ie || idx_ie[1] < 3) 509162306a36Sopenharmony_ci return valid; 509262306a36Sopenharmony_ci 509362306a36Sopenharmony_ci idx = (void *)(idx_ie + 2); 509462306a36Sopenharmony_ci 509562306a36Sopenharmony_ci if (dtim_count) 509662306a36Sopenharmony_ci *dtim_count = idx->dtim_count; 509762306a36Sopenharmony_ci 509862306a36Sopenharmony_ci if (dtim_period) 509962306a36Sopenharmony_ci *dtim_period = idx->dtim_period; 510062306a36Sopenharmony_ci 510162306a36Sopenharmony_ci return true; 510262306a36Sopenharmony_ci} 510362306a36Sopenharmony_ci 510462306a36Sopenharmony_cistatic bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, 510562306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, 510662306a36Sopenharmony_ci struct ieee802_11_elems *elems, 510762306a36Sopenharmony_ci const u8 *elem_start, unsigned int elem_len) 510862306a36Sopenharmony_ci{ 510962306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 511062306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; 511162306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 511262306a36Sopenharmony_ci unsigned int link_id; 511362306a36Sopenharmony_ci struct sta_info *sta; 511462306a36Sopenharmony_ci u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {}; 511562306a36Sopenharmony_ci u16 valid_links = 0, dormant_links = 0; 511662306a36Sopenharmony_ci int err; 511762306a36Sopenharmony_ci 511862306a36Sopenharmony_ci mutex_lock(&sdata->local->sta_mtx); 511962306a36Sopenharmony_ci /* 512062306a36Sopenharmony_ci * station info was already allocated and inserted before 512162306a36Sopenharmony_ci * the association and should be available to us 512262306a36Sopenharmony_ci */ 512362306a36Sopenharmony_ci sta = sta_info_get(sdata, assoc_data->ap_addr); 512462306a36Sopenharmony_ci if (WARN_ON(!sta)) 512562306a36Sopenharmony_ci goto out_err; 512662306a36Sopenharmony_ci 512762306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 512862306a36Sopenharmony_ci for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 512962306a36Sopenharmony_ci if (!assoc_data->link[link_id].bss) 513062306a36Sopenharmony_ci continue; 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_ci valid_links |= BIT(link_id); 513362306a36Sopenharmony_ci if (assoc_data->link[link_id].disabled) 513462306a36Sopenharmony_ci dormant_links |= BIT(link_id); 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci if (link_id != assoc_data->assoc_link_id) { 513762306a36Sopenharmony_ci err = ieee80211_sta_allocate_link(sta, link_id); 513862306a36Sopenharmony_ci if (err) 513962306a36Sopenharmony_ci goto out_err; 514062306a36Sopenharmony_ci } 514162306a36Sopenharmony_ci } 514262306a36Sopenharmony_ci 514362306a36Sopenharmony_ci ieee80211_vif_set_links(sdata, valid_links, dormant_links); 514462306a36Sopenharmony_ci } 514562306a36Sopenharmony_ci 514662306a36Sopenharmony_ci for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 514762306a36Sopenharmony_ci struct cfg80211_bss *cbss = assoc_data->link[link_id].bss; 514862306a36Sopenharmony_ci struct ieee80211_link_data *link; 514962306a36Sopenharmony_ci struct link_sta_info *link_sta; 515062306a36Sopenharmony_ci 515162306a36Sopenharmony_ci if (!cbss) 515262306a36Sopenharmony_ci continue; 515362306a36Sopenharmony_ci 515462306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 515562306a36Sopenharmony_ci if (WARN_ON(!link)) 515662306a36Sopenharmony_ci goto out_err; 515762306a36Sopenharmony_ci 515862306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) 515962306a36Sopenharmony_ci link_info(link, 516062306a36Sopenharmony_ci "local address %pM, AP link address %pM%s\n", 516162306a36Sopenharmony_ci link->conf->addr, 516262306a36Sopenharmony_ci assoc_data->link[link_id].bss->bssid, 516362306a36Sopenharmony_ci link_id == assoc_data->assoc_link_id ? 516462306a36Sopenharmony_ci " (assoc)" : ""); 516562306a36Sopenharmony_ci 516662306a36Sopenharmony_ci link_sta = rcu_dereference_protected(sta->link[link_id], 516762306a36Sopenharmony_ci lockdep_is_held(&local->sta_mtx)); 516862306a36Sopenharmony_ci if (WARN_ON(!link_sta)) 516962306a36Sopenharmony_ci goto out_err; 517062306a36Sopenharmony_ci 517162306a36Sopenharmony_ci if (!link->u.mgd.have_beacon) { 517262306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 517362306a36Sopenharmony_ci 517462306a36Sopenharmony_ci rcu_read_lock(); 517562306a36Sopenharmony_ci ies = rcu_dereference(cbss->beacon_ies); 517662306a36Sopenharmony_ci if (ies) 517762306a36Sopenharmony_ci link->u.mgd.have_beacon = true; 517862306a36Sopenharmony_ci else 517962306a36Sopenharmony_ci ies = rcu_dereference(cbss->ies); 518062306a36Sopenharmony_ci ieee80211_get_dtim(ies, 518162306a36Sopenharmony_ci &link->conf->sync_dtim_count, 518262306a36Sopenharmony_ci &link->u.mgd.dtim_period); 518362306a36Sopenharmony_ci link->conf->beacon_int = cbss->beacon_interval; 518462306a36Sopenharmony_ci rcu_read_unlock(); 518562306a36Sopenharmony_ci } 518662306a36Sopenharmony_ci 518762306a36Sopenharmony_ci link->conf->dtim_period = link->u.mgd.dtim_period ?: 1; 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ci if (link_id != assoc_data->assoc_link_id) { 519062306a36Sopenharmony_ci err = ieee80211_prep_channel(sdata, link, cbss, 519162306a36Sopenharmony_ci &link->u.mgd.conn_flags); 519262306a36Sopenharmony_ci if (err) { 519362306a36Sopenharmony_ci link_info(link, "prep_channel failed\n"); 519462306a36Sopenharmony_ci goto out_err; 519562306a36Sopenharmony_ci } 519662306a36Sopenharmony_ci } 519762306a36Sopenharmony_ci 519862306a36Sopenharmony_ci err = ieee80211_mgd_setup_link_sta(link, sta, link_sta, 519962306a36Sopenharmony_ci assoc_data->link[link_id].bss); 520062306a36Sopenharmony_ci if (err) 520162306a36Sopenharmony_ci goto out_err; 520262306a36Sopenharmony_ci 520362306a36Sopenharmony_ci if (!ieee80211_assoc_config_link(link, link_sta, 520462306a36Sopenharmony_ci assoc_data->link[link_id].bss, 520562306a36Sopenharmony_ci mgmt, elem_start, elem_len, 520662306a36Sopenharmony_ci &changed[link_id])) 520762306a36Sopenharmony_ci goto out_err; 520862306a36Sopenharmony_ci 520962306a36Sopenharmony_ci if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) { 521062306a36Sopenharmony_ci valid_links &= ~BIT(link_id); 521162306a36Sopenharmony_ci ieee80211_sta_remove_link(sta, link_id); 521262306a36Sopenharmony_ci continue; 521362306a36Sopenharmony_ci } 521462306a36Sopenharmony_ci 521562306a36Sopenharmony_ci if (link_id != assoc_data->assoc_link_id) { 521662306a36Sopenharmony_ci err = ieee80211_sta_activate_link(sta, link_id); 521762306a36Sopenharmony_ci if (err) 521862306a36Sopenharmony_ci goto out_err; 521962306a36Sopenharmony_ci } 522062306a36Sopenharmony_ci } 522162306a36Sopenharmony_ci 522262306a36Sopenharmony_ci /* links might have changed due to rejected ones, set them again */ 522362306a36Sopenharmony_ci ieee80211_vif_set_links(sdata, valid_links, dormant_links); 522462306a36Sopenharmony_ci 522562306a36Sopenharmony_ci rate_control_rate_init(sta); 522662306a36Sopenharmony_ci 522762306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) { 522862306a36Sopenharmony_ci set_sta_flag(sta, WLAN_STA_MFP); 522962306a36Sopenharmony_ci sta->sta.mfp = true; 523062306a36Sopenharmony_ci } else { 523162306a36Sopenharmony_ci sta->sta.mfp = false; 523262306a36Sopenharmony_ci } 523362306a36Sopenharmony_ci 523462306a36Sopenharmony_ci ieee80211_sta_set_max_amsdu_subframes(sta, elems->ext_capab, 523562306a36Sopenharmony_ci elems->ext_capab_len); 523662306a36Sopenharmony_ci 523762306a36Sopenharmony_ci sta->sta.wme = (elems->wmm_param || elems->s1g_capab) && 523862306a36Sopenharmony_ci local->hw.queues >= IEEE80211_NUM_ACS; 523962306a36Sopenharmony_ci 524062306a36Sopenharmony_ci err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); 524162306a36Sopenharmony_ci if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) 524262306a36Sopenharmony_ci err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); 524362306a36Sopenharmony_ci if (err) { 524462306a36Sopenharmony_ci sdata_info(sdata, 524562306a36Sopenharmony_ci "failed to move station %pM to desired state\n", 524662306a36Sopenharmony_ci sta->sta.addr); 524762306a36Sopenharmony_ci WARN_ON(__sta_info_destroy(sta)); 524862306a36Sopenharmony_ci goto out_err; 524962306a36Sopenharmony_ci } 525062306a36Sopenharmony_ci 525162306a36Sopenharmony_ci if (sdata->wdev.use_4addr) 525262306a36Sopenharmony_ci drv_sta_set_4addr(local, sdata, &sta->sta, true); 525362306a36Sopenharmony_ci 525462306a36Sopenharmony_ci mutex_unlock(&sdata->local->sta_mtx); 525562306a36Sopenharmony_ci 525662306a36Sopenharmony_ci ieee80211_set_associated(sdata, assoc_data, changed); 525762306a36Sopenharmony_ci 525862306a36Sopenharmony_ci /* 525962306a36Sopenharmony_ci * If we're using 4-addr mode, let the AP know that we're 526062306a36Sopenharmony_ci * doing so, so that it can create the STA VLAN on its side 526162306a36Sopenharmony_ci */ 526262306a36Sopenharmony_ci if (ifmgd->use_4addr) 526362306a36Sopenharmony_ci ieee80211_send_4addr_nullfunc(local, sdata); 526462306a36Sopenharmony_ci 526562306a36Sopenharmony_ci /* 526662306a36Sopenharmony_ci * Start timer to probe the connection to the AP now. 526762306a36Sopenharmony_ci * Also start the timer that will detect beacon loss. 526862306a36Sopenharmony_ci */ 526962306a36Sopenharmony_ci ieee80211_sta_reset_beacon_monitor(sdata); 527062306a36Sopenharmony_ci ieee80211_sta_reset_conn_monitor(sdata); 527162306a36Sopenharmony_ci 527262306a36Sopenharmony_ci return true; 527362306a36Sopenharmony_ciout_err: 527462306a36Sopenharmony_ci eth_zero_addr(sdata->vif.cfg.ap_addr); 527562306a36Sopenharmony_ci mutex_unlock(&sdata->local->sta_mtx); 527662306a36Sopenharmony_ci return false; 527762306a36Sopenharmony_ci} 527862306a36Sopenharmony_ci 527962306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, 528062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, 528162306a36Sopenharmony_ci size_t len) 528262306a36Sopenharmony_ci{ 528362306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 528462306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data; 528562306a36Sopenharmony_ci u16 capab_info, status_code, aid; 528662306a36Sopenharmony_ci struct ieee80211_elems_parse_params parse_params = { 528762306a36Sopenharmony_ci .bss = NULL, 528862306a36Sopenharmony_ci .link_id = -1, 528962306a36Sopenharmony_ci .from_ap = true, 529062306a36Sopenharmony_ci }; 529162306a36Sopenharmony_ci struct ieee802_11_elems *elems; 529262306a36Sopenharmony_ci int ac; 529362306a36Sopenharmony_ci const u8 *elem_start; 529462306a36Sopenharmony_ci unsigned int elem_len; 529562306a36Sopenharmony_ci bool reassoc; 529662306a36Sopenharmony_ci struct ieee80211_event event = { 529762306a36Sopenharmony_ci .type = MLME_EVENT, 529862306a36Sopenharmony_ci .u.mlme.data = ASSOC_EVENT, 529962306a36Sopenharmony_ci }; 530062306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = {}; 530162306a36Sopenharmony_ci struct cfg80211_rx_assoc_resp resp = { 530262306a36Sopenharmony_ci .uapsd_queues = -1, 530362306a36Sopenharmony_ci }; 530462306a36Sopenharmony_ci u8 ap_mld_addr[ETH_ALEN] __aligned(2); 530562306a36Sopenharmony_ci unsigned int link_id; 530662306a36Sopenharmony_ci 530762306a36Sopenharmony_ci sdata_assert_lock(sdata); 530862306a36Sopenharmony_ci 530962306a36Sopenharmony_ci if (!assoc_data) 531062306a36Sopenharmony_ci return; 531162306a36Sopenharmony_ci 531262306a36Sopenharmony_ci if (!ether_addr_equal(assoc_data->ap_addr, mgmt->bssid) || 531362306a36Sopenharmony_ci !ether_addr_equal(assoc_data->ap_addr, mgmt->sa)) 531462306a36Sopenharmony_ci return; 531562306a36Sopenharmony_ci 531662306a36Sopenharmony_ci /* 531762306a36Sopenharmony_ci * AssocResp and ReassocResp have identical structure, so process both 531862306a36Sopenharmony_ci * of them in this function. 531962306a36Sopenharmony_ci */ 532062306a36Sopenharmony_ci 532162306a36Sopenharmony_ci if (len < 24 + 6) 532262306a36Sopenharmony_ci return; 532362306a36Sopenharmony_ci 532462306a36Sopenharmony_ci reassoc = ieee80211_is_reassoc_resp(mgmt->frame_control); 532562306a36Sopenharmony_ci capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); 532662306a36Sopenharmony_ci status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); 532762306a36Sopenharmony_ci if (assoc_data->s1g) 532862306a36Sopenharmony_ci elem_start = mgmt->u.s1g_assoc_resp.variable; 532962306a36Sopenharmony_ci else 533062306a36Sopenharmony_ci elem_start = mgmt->u.assoc_resp.variable; 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci /* 533362306a36Sopenharmony_ci * Note: this may not be perfect, AP might misbehave - if 533462306a36Sopenharmony_ci * anyone needs to rely on perfect complete notification 533562306a36Sopenharmony_ci * with the exact right subtype, then we need to track what 533662306a36Sopenharmony_ci * we actually transmitted. 533762306a36Sopenharmony_ci */ 533862306a36Sopenharmony_ci info.subtype = reassoc ? IEEE80211_STYPE_REASSOC_REQ : 533962306a36Sopenharmony_ci IEEE80211_STYPE_ASSOC_REQ; 534062306a36Sopenharmony_ci 534162306a36Sopenharmony_ci if (assoc_data->fils_kek_len && 534262306a36Sopenharmony_ci fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0) 534362306a36Sopenharmony_ci return; 534462306a36Sopenharmony_ci 534562306a36Sopenharmony_ci elem_len = len - (elem_start - (u8 *)mgmt); 534662306a36Sopenharmony_ci parse_params.start = elem_start; 534762306a36Sopenharmony_ci parse_params.len = elem_len; 534862306a36Sopenharmony_ci elems = ieee802_11_parse_elems_full(&parse_params); 534962306a36Sopenharmony_ci if (!elems) 535062306a36Sopenharmony_ci goto notify_driver; 535162306a36Sopenharmony_ci 535262306a36Sopenharmony_ci if (elems->aid_resp) 535362306a36Sopenharmony_ci aid = le16_to_cpu(elems->aid_resp->aid); 535462306a36Sopenharmony_ci else if (assoc_data->s1g) 535562306a36Sopenharmony_ci aid = 0; /* TODO */ 535662306a36Sopenharmony_ci else 535762306a36Sopenharmony_ci aid = le16_to_cpu(mgmt->u.assoc_resp.aid); 535862306a36Sopenharmony_ci 535962306a36Sopenharmony_ci /* 536062306a36Sopenharmony_ci * The 5 MSB of the AID field are reserved 536162306a36Sopenharmony_ci * (802.11-2016 9.4.1.8 AID field) 536262306a36Sopenharmony_ci */ 536362306a36Sopenharmony_ci aid &= 0x7ff; 536462306a36Sopenharmony_ci 536562306a36Sopenharmony_ci sdata_info(sdata, 536662306a36Sopenharmony_ci "RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n", 536762306a36Sopenharmony_ci reassoc ? "Rea" : "A", assoc_data->ap_addr, 536862306a36Sopenharmony_ci capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14)))); 536962306a36Sopenharmony_ci 537062306a36Sopenharmony_ci ifmgd->broken_ap = false; 537162306a36Sopenharmony_ci 537262306a36Sopenharmony_ci if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY && 537362306a36Sopenharmony_ci elems->timeout_int && 537462306a36Sopenharmony_ci elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) { 537562306a36Sopenharmony_ci u32 tu, ms; 537662306a36Sopenharmony_ci 537762306a36Sopenharmony_ci cfg80211_assoc_comeback(sdata->dev, assoc_data->ap_addr, 537862306a36Sopenharmony_ci le32_to_cpu(elems->timeout_int->value)); 537962306a36Sopenharmony_ci 538062306a36Sopenharmony_ci tu = le32_to_cpu(elems->timeout_int->value); 538162306a36Sopenharmony_ci ms = tu * 1024 / 1000; 538262306a36Sopenharmony_ci sdata_info(sdata, 538362306a36Sopenharmony_ci "%pM rejected association temporarily; comeback duration %u TU (%u ms)\n", 538462306a36Sopenharmony_ci assoc_data->ap_addr, tu, ms); 538562306a36Sopenharmony_ci assoc_data->timeout = jiffies + msecs_to_jiffies(ms); 538662306a36Sopenharmony_ci assoc_data->timeout_started = true; 538762306a36Sopenharmony_ci if (ms > IEEE80211_ASSOC_TIMEOUT) 538862306a36Sopenharmony_ci run_again(sdata, assoc_data->timeout); 538962306a36Sopenharmony_ci goto notify_driver; 539062306a36Sopenharmony_ci } 539162306a36Sopenharmony_ci 539262306a36Sopenharmony_ci if (status_code != WLAN_STATUS_SUCCESS) { 539362306a36Sopenharmony_ci sdata_info(sdata, "%pM denied association (code=%d)\n", 539462306a36Sopenharmony_ci assoc_data->ap_addr, status_code); 539562306a36Sopenharmony_ci event.u.mlme.status = MLME_DENIED; 539662306a36Sopenharmony_ci event.u.mlme.reason = status_code; 539762306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 539862306a36Sopenharmony_ci } else { 539962306a36Sopenharmony_ci if (aid == 0 || aid > IEEE80211_MAX_AID) { 540062306a36Sopenharmony_ci sdata_info(sdata, 540162306a36Sopenharmony_ci "invalid AID value %d (out of range), turn off PS\n", 540262306a36Sopenharmony_ci aid); 540362306a36Sopenharmony_ci aid = 0; 540462306a36Sopenharmony_ci ifmgd->broken_ap = true; 540562306a36Sopenharmony_ci } 540662306a36Sopenharmony_ci 540762306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 540862306a36Sopenharmony_ci if (!elems->ml_basic) { 540962306a36Sopenharmony_ci sdata_info(sdata, 541062306a36Sopenharmony_ci "MLO association with %pM but no multi-link element in response!\n", 541162306a36Sopenharmony_ci assoc_data->ap_addr); 541262306a36Sopenharmony_ci goto abandon_assoc; 541362306a36Sopenharmony_ci } 541462306a36Sopenharmony_ci 541562306a36Sopenharmony_ci if (le16_get_bits(elems->ml_basic->control, 541662306a36Sopenharmony_ci IEEE80211_ML_CONTROL_TYPE) != 541762306a36Sopenharmony_ci IEEE80211_ML_CONTROL_TYPE_BASIC) { 541862306a36Sopenharmony_ci sdata_info(sdata, 541962306a36Sopenharmony_ci "bad multi-link element (control=0x%x)\n", 542062306a36Sopenharmony_ci le16_to_cpu(elems->ml_basic->control)); 542162306a36Sopenharmony_ci goto abandon_assoc; 542262306a36Sopenharmony_ci } else { 542362306a36Sopenharmony_ci struct ieee80211_mle_basic_common_info *common; 542462306a36Sopenharmony_ci 542562306a36Sopenharmony_ci common = (void *)elems->ml_basic->variable; 542662306a36Sopenharmony_ci 542762306a36Sopenharmony_ci if (memcmp(assoc_data->ap_addr, 542862306a36Sopenharmony_ci common->mld_mac_addr, ETH_ALEN)) { 542962306a36Sopenharmony_ci sdata_info(sdata, 543062306a36Sopenharmony_ci "AP MLD MAC address mismatch: got %pM expected %pM\n", 543162306a36Sopenharmony_ci common->mld_mac_addr, 543262306a36Sopenharmony_ci assoc_data->ap_addr); 543362306a36Sopenharmony_ci goto abandon_assoc; 543462306a36Sopenharmony_ci } 543562306a36Sopenharmony_ci } 543662306a36Sopenharmony_ci } 543762306a36Sopenharmony_ci 543862306a36Sopenharmony_ci sdata->vif.cfg.aid = aid; 543962306a36Sopenharmony_ci 544062306a36Sopenharmony_ci if (!ieee80211_assoc_success(sdata, mgmt, elems, 544162306a36Sopenharmony_ci elem_start, elem_len)) { 544262306a36Sopenharmony_ci /* oops -- internal error -- send timeout for now */ 544362306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); 544462306a36Sopenharmony_ci goto notify_driver; 544562306a36Sopenharmony_ci } 544662306a36Sopenharmony_ci event.u.mlme.status = MLME_SUCCESS; 544762306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 544862306a36Sopenharmony_ci sdata_info(sdata, "associated\n"); 544962306a36Sopenharmony_ci 545062306a36Sopenharmony_ci info.success = 1; 545162306a36Sopenharmony_ci } 545262306a36Sopenharmony_ci 545362306a36Sopenharmony_ci for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { 545462306a36Sopenharmony_ci struct ieee80211_link_data *link; 545562306a36Sopenharmony_ci 545662306a36Sopenharmony_ci if (!assoc_data->link[link_id].bss) 545762306a36Sopenharmony_ci continue; 545862306a36Sopenharmony_ci 545962306a36Sopenharmony_ci resp.links[link_id].bss = assoc_data->link[link_id].bss; 546062306a36Sopenharmony_ci ether_addr_copy(resp.links[link_id].addr, 546162306a36Sopenharmony_ci assoc_data->link[link_id].addr); 546262306a36Sopenharmony_ci resp.links[link_id].status = assoc_data->link[link_id].status; 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 546562306a36Sopenharmony_ci if (!link) 546662306a36Sopenharmony_ci continue; 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_ci /* get uapsd queues configuration - same for all links */ 546962306a36Sopenharmony_ci resp.uapsd_queues = 0; 547062306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 547162306a36Sopenharmony_ci if (link->tx_conf[ac].uapsd) 547262306a36Sopenharmony_ci resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac]; 547362306a36Sopenharmony_ci } 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 547662306a36Sopenharmony_ci ether_addr_copy(ap_mld_addr, sdata->vif.cfg.ap_addr); 547762306a36Sopenharmony_ci resp.ap_mld_addr = ap_mld_addr; 547862306a36Sopenharmony_ci } 547962306a36Sopenharmony_ci 548062306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, 548162306a36Sopenharmony_ci status_code == WLAN_STATUS_SUCCESS ? 548262306a36Sopenharmony_ci ASSOC_SUCCESS : 548362306a36Sopenharmony_ci ASSOC_REJECTED); 548462306a36Sopenharmony_ci 548562306a36Sopenharmony_ci resp.buf = (u8 *)mgmt; 548662306a36Sopenharmony_ci resp.len = len; 548762306a36Sopenharmony_ci resp.req_ies = ifmgd->assoc_req_ies; 548862306a36Sopenharmony_ci resp.req_ies_len = ifmgd->assoc_req_ies_len; 548962306a36Sopenharmony_ci cfg80211_rx_assoc_resp(sdata->dev, &resp); 549062306a36Sopenharmony_cinotify_driver: 549162306a36Sopenharmony_ci drv_mgd_complete_tx(sdata->local, sdata, &info); 549262306a36Sopenharmony_ci kfree(elems); 549362306a36Sopenharmony_ci return; 549462306a36Sopenharmony_ciabandon_assoc: 549562306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON); 549662306a36Sopenharmony_ci goto notify_driver; 549762306a36Sopenharmony_ci} 549862306a36Sopenharmony_ci 549962306a36Sopenharmony_cistatic void ieee80211_rx_bss_info(struct ieee80211_link_data *link, 550062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt, size_t len, 550162306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status) 550262306a36Sopenharmony_ci{ 550362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 550462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 550562306a36Sopenharmony_ci struct ieee80211_bss *bss; 550662306a36Sopenharmony_ci struct ieee80211_channel *channel; 550762306a36Sopenharmony_ci 550862306a36Sopenharmony_ci sdata_assert_lock(sdata); 550962306a36Sopenharmony_ci 551062306a36Sopenharmony_ci channel = ieee80211_get_channel_khz(local->hw.wiphy, 551162306a36Sopenharmony_ci ieee80211_rx_status_to_khz(rx_status)); 551262306a36Sopenharmony_ci if (!channel) 551362306a36Sopenharmony_ci return; 551462306a36Sopenharmony_ci 551562306a36Sopenharmony_ci bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel); 551662306a36Sopenharmony_ci if (bss) { 551762306a36Sopenharmony_ci link->conf->beacon_rate = bss->beacon_rate; 551862306a36Sopenharmony_ci ieee80211_rx_bss_put(local, bss); 551962306a36Sopenharmony_ci } 552062306a36Sopenharmony_ci} 552162306a36Sopenharmony_ci 552262306a36Sopenharmony_ci 552362306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_probe_resp(struct ieee80211_link_data *link, 552462306a36Sopenharmony_ci struct sk_buff *skb) 552562306a36Sopenharmony_ci{ 552662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 552762306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *)skb->data; 552862306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd; 552962306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status = (void *) skb->cb; 553062306a36Sopenharmony_ci struct ieee80211_channel *channel; 553162306a36Sopenharmony_ci size_t baselen, len = skb->len; 553262306a36Sopenharmony_ci 553362306a36Sopenharmony_ci ifmgd = &sdata->u.mgd; 553462306a36Sopenharmony_ci 553562306a36Sopenharmony_ci sdata_assert_lock(sdata); 553662306a36Sopenharmony_ci 553762306a36Sopenharmony_ci /* 553862306a36Sopenharmony_ci * According to Draft P802.11ax D6.0 clause 26.17.2.3.2: 553962306a36Sopenharmony_ci * "If a 6 GHz AP receives a Probe Request frame and responds with 554062306a36Sopenharmony_ci * a Probe Response frame [..], the Address 1 field of the Probe 554162306a36Sopenharmony_ci * Response frame shall be set to the broadcast address [..]" 554262306a36Sopenharmony_ci * So, on 6GHz band we should also accept broadcast responses. 554362306a36Sopenharmony_ci */ 554462306a36Sopenharmony_ci channel = ieee80211_get_channel(sdata->local->hw.wiphy, 554562306a36Sopenharmony_ci rx_status->freq); 554662306a36Sopenharmony_ci if (!channel) 554762306a36Sopenharmony_ci return; 554862306a36Sopenharmony_ci 554962306a36Sopenharmony_ci if (!ether_addr_equal(mgmt->da, sdata->vif.addr) && 555062306a36Sopenharmony_ci (channel->band != NL80211_BAND_6GHZ || 555162306a36Sopenharmony_ci !is_broadcast_ether_addr(mgmt->da))) 555262306a36Sopenharmony_ci return; /* ignore ProbeResp to foreign address */ 555362306a36Sopenharmony_ci 555462306a36Sopenharmony_ci baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt; 555562306a36Sopenharmony_ci if (baselen > len) 555662306a36Sopenharmony_ci return; 555762306a36Sopenharmony_ci 555862306a36Sopenharmony_ci ieee80211_rx_bss_info(link, mgmt, len, rx_status); 555962306a36Sopenharmony_ci 556062306a36Sopenharmony_ci if (ifmgd->associated && 556162306a36Sopenharmony_ci ether_addr_equal(mgmt->bssid, link->u.mgd.bssid)) 556262306a36Sopenharmony_ci ieee80211_reset_ap_probe(sdata); 556362306a36Sopenharmony_ci} 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_ci/* 556662306a36Sopenharmony_ci * This is the canonical list of information elements we care about, 556762306a36Sopenharmony_ci * the filter code also gives us all changes to the Microsoft OUI 556862306a36Sopenharmony_ci * (00:50:F2) vendor IE which is used for WMM which we need to track, 556962306a36Sopenharmony_ci * as well as the DTPC IE (part of the Cisco OUI) used for signaling 557062306a36Sopenharmony_ci * changes to requested client power. 557162306a36Sopenharmony_ci * 557262306a36Sopenharmony_ci * We implement beacon filtering in software since that means we can 557362306a36Sopenharmony_ci * avoid processing the frame here and in cfg80211, and userspace 557462306a36Sopenharmony_ci * will not be able to tell whether the hardware supports it or not. 557562306a36Sopenharmony_ci * 557662306a36Sopenharmony_ci * XXX: This list needs to be dynamic -- userspace needs to be able to 557762306a36Sopenharmony_ci * add items it requires. It also needs to be able to tell us to 557862306a36Sopenharmony_ci * look out for other vendor IEs. 557962306a36Sopenharmony_ci */ 558062306a36Sopenharmony_cistatic const u64 care_about_ies = 558162306a36Sopenharmony_ci (1ULL << WLAN_EID_COUNTRY) | 558262306a36Sopenharmony_ci (1ULL << WLAN_EID_ERP_INFO) | 558362306a36Sopenharmony_ci (1ULL << WLAN_EID_CHANNEL_SWITCH) | 558462306a36Sopenharmony_ci (1ULL << WLAN_EID_PWR_CONSTRAINT) | 558562306a36Sopenharmony_ci (1ULL << WLAN_EID_HT_CAPABILITY) | 558662306a36Sopenharmony_ci (1ULL << WLAN_EID_HT_OPERATION) | 558762306a36Sopenharmony_ci (1ULL << WLAN_EID_EXT_CHANSWITCH_ANN); 558862306a36Sopenharmony_ci 558962306a36Sopenharmony_cistatic void ieee80211_handle_beacon_sig(struct ieee80211_link_data *link, 559062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd, 559162306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, 559262306a36Sopenharmony_ci struct ieee80211_local *local, 559362306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status) 559462306a36Sopenharmony_ci{ 559562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 559662306a36Sopenharmony_ci 559762306a36Sopenharmony_ci /* Track average RSSI from the Beacon frames of the current AP */ 559862306a36Sopenharmony_ci 559962306a36Sopenharmony_ci if (!link->u.mgd.tracking_signal_avg) { 560062306a36Sopenharmony_ci link->u.mgd.tracking_signal_avg = true; 560162306a36Sopenharmony_ci ewma_beacon_signal_init(&link->u.mgd.ave_beacon_signal); 560262306a36Sopenharmony_ci link->u.mgd.last_cqm_event_signal = 0; 560362306a36Sopenharmony_ci link->u.mgd.count_beacon_signal = 1; 560462306a36Sopenharmony_ci link->u.mgd.last_ave_beacon_signal = 0; 560562306a36Sopenharmony_ci } else { 560662306a36Sopenharmony_ci link->u.mgd.count_beacon_signal++; 560762306a36Sopenharmony_ci } 560862306a36Sopenharmony_ci 560962306a36Sopenharmony_ci ewma_beacon_signal_add(&link->u.mgd.ave_beacon_signal, 561062306a36Sopenharmony_ci -rx_status->signal); 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_ci if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold && 561362306a36Sopenharmony_ci link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { 561462306a36Sopenharmony_ci int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal); 561562306a36Sopenharmony_ci int last_sig = link->u.mgd.last_ave_beacon_signal; 561662306a36Sopenharmony_ci struct ieee80211_event event = { 561762306a36Sopenharmony_ci .type = RSSI_EVENT, 561862306a36Sopenharmony_ci }; 561962306a36Sopenharmony_ci 562062306a36Sopenharmony_ci /* 562162306a36Sopenharmony_ci * if signal crosses either of the boundaries, invoke callback 562262306a36Sopenharmony_ci * with appropriate parameters 562362306a36Sopenharmony_ci */ 562462306a36Sopenharmony_ci if (sig > ifmgd->rssi_max_thold && 562562306a36Sopenharmony_ci (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { 562662306a36Sopenharmony_ci link->u.mgd.last_ave_beacon_signal = sig; 562762306a36Sopenharmony_ci event.u.rssi.data = RSSI_EVENT_HIGH; 562862306a36Sopenharmony_ci drv_event_callback(local, sdata, &event); 562962306a36Sopenharmony_ci } else if (sig < ifmgd->rssi_min_thold && 563062306a36Sopenharmony_ci (last_sig >= ifmgd->rssi_max_thold || 563162306a36Sopenharmony_ci last_sig == 0)) { 563262306a36Sopenharmony_ci link->u.mgd.last_ave_beacon_signal = sig; 563362306a36Sopenharmony_ci event.u.rssi.data = RSSI_EVENT_LOW; 563462306a36Sopenharmony_ci drv_event_callback(local, sdata, &event); 563562306a36Sopenharmony_ci } 563662306a36Sopenharmony_ci } 563762306a36Sopenharmony_ci 563862306a36Sopenharmony_ci if (bss_conf->cqm_rssi_thold && 563962306a36Sopenharmony_ci link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT && 564062306a36Sopenharmony_ci !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) { 564162306a36Sopenharmony_ci int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal); 564262306a36Sopenharmony_ci int last_event = link->u.mgd.last_cqm_event_signal; 564362306a36Sopenharmony_ci int thold = bss_conf->cqm_rssi_thold; 564462306a36Sopenharmony_ci int hyst = bss_conf->cqm_rssi_hyst; 564562306a36Sopenharmony_ci 564662306a36Sopenharmony_ci if (sig < thold && 564762306a36Sopenharmony_ci (last_event == 0 || sig < last_event - hyst)) { 564862306a36Sopenharmony_ci link->u.mgd.last_cqm_event_signal = sig; 564962306a36Sopenharmony_ci ieee80211_cqm_rssi_notify( 565062306a36Sopenharmony_ci &sdata->vif, 565162306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, 565262306a36Sopenharmony_ci sig, GFP_KERNEL); 565362306a36Sopenharmony_ci } else if (sig > thold && 565462306a36Sopenharmony_ci (last_event == 0 || sig > last_event + hyst)) { 565562306a36Sopenharmony_ci link->u.mgd.last_cqm_event_signal = sig; 565662306a36Sopenharmony_ci ieee80211_cqm_rssi_notify( 565762306a36Sopenharmony_ci &sdata->vif, 565862306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, 565962306a36Sopenharmony_ci sig, GFP_KERNEL); 566062306a36Sopenharmony_ci } 566162306a36Sopenharmony_ci } 566262306a36Sopenharmony_ci 566362306a36Sopenharmony_ci if (bss_conf->cqm_rssi_low && 566462306a36Sopenharmony_ci link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { 566562306a36Sopenharmony_ci int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal); 566662306a36Sopenharmony_ci int last_event = link->u.mgd.last_cqm_event_signal; 566762306a36Sopenharmony_ci int low = bss_conf->cqm_rssi_low; 566862306a36Sopenharmony_ci int high = bss_conf->cqm_rssi_high; 566962306a36Sopenharmony_ci 567062306a36Sopenharmony_ci if (sig < low && 567162306a36Sopenharmony_ci (last_event == 0 || last_event >= low)) { 567262306a36Sopenharmony_ci link->u.mgd.last_cqm_event_signal = sig; 567362306a36Sopenharmony_ci ieee80211_cqm_rssi_notify( 567462306a36Sopenharmony_ci &sdata->vif, 567562306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, 567662306a36Sopenharmony_ci sig, GFP_KERNEL); 567762306a36Sopenharmony_ci } else if (sig > high && 567862306a36Sopenharmony_ci (last_event == 0 || last_event <= high)) { 567962306a36Sopenharmony_ci link->u.mgd.last_cqm_event_signal = sig; 568062306a36Sopenharmony_ci ieee80211_cqm_rssi_notify( 568162306a36Sopenharmony_ci &sdata->vif, 568262306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, 568362306a36Sopenharmony_ci sig, GFP_KERNEL); 568462306a36Sopenharmony_ci } 568562306a36Sopenharmony_ci } 568662306a36Sopenharmony_ci} 568762306a36Sopenharmony_ci 568862306a36Sopenharmony_cistatic bool ieee80211_rx_our_beacon(const u8 *tx_bssid, 568962306a36Sopenharmony_ci struct cfg80211_bss *bss) 569062306a36Sopenharmony_ci{ 569162306a36Sopenharmony_ci if (ether_addr_equal(tx_bssid, bss->bssid)) 569262306a36Sopenharmony_ci return true; 569362306a36Sopenharmony_ci if (!bss->transmitted_bss) 569462306a36Sopenharmony_ci return false; 569562306a36Sopenharmony_ci return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid); 569662306a36Sopenharmony_ci} 569762306a36Sopenharmony_ci 569862306a36Sopenharmony_cistatic bool ieee80211_config_puncturing(struct ieee80211_link_data *link, 569962306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper, 570062306a36Sopenharmony_ci u64 *changed) 570162306a36Sopenharmony_ci{ 570262306a36Sopenharmony_ci u16 bitmap = 0, extracted; 570362306a36Sopenharmony_ci 570462306a36Sopenharmony_ci if ((eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) && 570562306a36Sopenharmony_ci (eht_oper->params & 570662306a36Sopenharmony_ci IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) { 570762306a36Sopenharmony_ci const struct ieee80211_eht_operation_info *info = 570862306a36Sopenharmony_ci (void *)eht_oper->optional; 570962306a36Sopenharmony_ci const u8 *disable_subchannel_bitmap = info->optional; 571062306a36Sopenharmony_ci 571162306a36Sopenharmony_ci bitmap = get_unaligned_le16(disable_subchannel_bitmap); 571262306a36Sopenharmony_ci } 571362306a36Sopenharmony_ci 571462306a36Sopenharmony_ci extracted = ieee80211_extract_dis_subch_bmap(eht_oper, 571562306a36Sopenharmony_ci &link->conf->chandef, 571662306a36Sopenharmony_ci bitmap); 571762306a36Sopenharmony_ci 571862306a36Sopenharmony_ci /* accept if there are no changes */ 571962306a36Sopenharmony_ci if (!(*changed & BSS_CHANGED_BANDWIDTH) && 572062306a36Sopenharmony_ci extracted == link->conf->eht_puncturing) 572162306a36Sopenharmony_ci return true; 572262306a36Sopenharmony_ci 572362306a36Sopenharmony_ci if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap, 572462306a36Sopenharmony_ci &link->conf->chandef)) { 572562306a36Sopenharmony_ci link_info(link, 572662306a36Sopenharmony_ci "Got an invalid disable subchannel bitmap from AP %pM: bitmap = 0x%x, bw = 0x%x. disconnect\n", 572762306a36Sopenharmony_ci link->u.mgd.bssid, 572862306a36Sopenharmony_ci bitmap, 572962306a36Sopenharmony_ci link->conf->chandef.width); 573062306a36Sopenharmony_ci return false; 573162306a36Sopenharmony_ci } 573262306a36Sopenharmony_ci 573362306a36Sopenharmony_ci ieee80211_handle_puncturing_bitmap(link, eht_oper, bitmap, changed); 573462306a36Sopenharmony_ci return true; 573562306a36Sopenharmony_ci} 573662306a36Sopenharmony_ci 573762306a36Sopenharmony_cistatic void ieee80211_ml_reconf_work(struct wiphy *wiphy, 573862306a36Sopenharmony_ci struct wiphy_work *work) 573962306a36Sopenharmony_ci{ 574062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 574162306a36Sopenharmony_ci container_of(work, struct ieee80211_sub_if_data, 574262306a36Sopenharmony_ci u.mgd.ml_reconf_work.work); 574362306a36Sopenharmony_ci u16 new_valid_links, new_active_links, new_dormant_links; 574462306a36Sopenharmony_ci int ret; 574562306a36Sopenharmony_ci 574662306a36Sopenharmony_ci sdata_lock(sdata); 574762306a36Sopenharmony_ci if (!sdata->u.mgd.removed_links) { 574862306a36Sopenharmony_ci sdata_unlock(sdata); 574962306a36Sopenharmony_ci return; 575062306a36Sopenharmony_ci } 575162306a36Sopenharmony_ci 575262306a36Sopenharmony_ci sdata_info(sdata, 575362306a36Sopenharmony_ci "MLO Reconfiguration: work: valid=0x%x, removed=0x%x\n", 575462306a36Sopenharmony_ci sdata->vif.valid_links, sdata->u.mgd.removed_links); 575562306a36Sopenharmony_ci 575662306a36Sopenharmony_ci new_valid_links = sdata->vif.valid_links & ~sdata->u.mgd.removed_links; 575762306a36Sopenharmony_ci if (new_valid_links == sdata->vif.valid_links) { 575862306a36Sopenharmony_ci sdata_unlock(sdata); 575962306a36Sopenharmony_ci return; 576062306a36Sopenharmony_ci } 576162306a36Sopenharmony_ci 576262306a36Sopenharmony_ci if (!new_valid_links || 576362306a36Sopenharmony_ci !(new_valid_links & ~sdata->vif.dormant_links)) { 576462306a36Sopenharmony_ci sdata_info(sdata, "No valid links after reconfiguration\n"); 576562306a36Sopenharmony_ci ret = -EINVAL; 576662306a36Sopenharmony_ci goto out; 576762306a36Sopenharmony_ci } 576862306a36Sopenharmony_ci 576962306a36Sopenharmony_ci new_active_links = sdata->vif.active_links & ~sdata->u.mgd.removed_links; 577062306a36Sopenharmony_ci if (new_active_links != sdata->vif.active_links) { 577162306a36Sopenharmony_ci if (!new_active_links) 577262306a36Sopenharmony_ci new_active_links = 577362306a36Sopenharmony_ci BIT(ffs(new_valid_links & 577462306a36Sopenharmony_ci ~sdata->vif.dormant_links) - 1); 577562306a36Sopenharmony_ci 577662306a36Sopenharmony_ci ret = __ieee80211_set_active_links(&sdata->vif, 577762306a36Sopenharmony_ci new_active_links); 577862306a36Sopenharmony_ci if (ret) { 577962306a36Sopenharmony_ci sdata_info(sdata, 578062306a36Sopenharmony_ci "Failed setting active links\n"); 578162306a36Sopenharmony_ci goto out; 578262306a36Sopenharmony_ci } 578362306a36Sopenharmony_ci } 578462306a36Sopenharmony_ci 578562306a36Sopenharmony_ci new_dormant_links = sdata->vif.dormant_links & ~sdata->u.mgd.removed_links; 578662306a36Sopenharmony_ci 578762306a36Sopenharmony_ci ret = ieee80211_vif_set_links(sdata, new_valid_links, 578862306a36Sopenharmony_ci new_dormant_links); 578962306a36Sopenharmony_ci if (ret) 579062306a36Sopenharmony_ci sdata_info(sdata, "Failed setting valid links\n"); 579162306a36Sopenharmony_ci 579262306a36Sopenharmony_ciout: 579362306a36Sopenharmony_ci if (!ret) 579462306a36Sopenharmony_ci cfg80211_links_removed(sdata->dev, sdata->u.mgd.removed_links); 579562306a36Sopenharmony_ci else 579662306a36Sopenharmony_ci ___ieee80211_disconnect(sdata); 579762306a36Sopenharmony_ci 579862306a36Sopenharmony_ci sdata->u.mgd.removed_links = 0; 579962306a36Sopenharmony_ci 580062306a36Sopenharmony_ci sdata_unlock(sdata); 580162306a36Sopenharmony_ci} 580262306a36Sopenharmony_ci 580362306a36Sopenharmony_cistatic void ieee80211_ml_reconfiguration(struct ieee80211_sub_if_data *sdata, 580462306a36Sopenharmony_ci struct ieee802_11_elems *elems) 580562306a36Sopenharmony_ci{ 580662306a36Sopenharmony_ci const struct ieee80211_multi_link_elem *ml; 580762306a36Sopenharmony_ci const struct element *sub; 580862306a36Sopenharmony_ci ssize_t ml_len; 580962306a36Sopenharmony_ci unsigned long removed_links = 0; 581062306a36Sopenharmony_ci u16 link_removal_timeout[IEEE80211_MLD_MAX_NUM_LINKS] = {}; 581162306a36Sopenharmony_ci u8 link_id; 581262306a36Sopenharmony_ci u32 delay; 581362306a36Sopenharmony_ci 581462306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif) || !elems->ml_reconf) 581562306a36Sopenharmony_ci return; 581662306a36Sopenharmony_ci 581762306a36Sopenharmony_ci ml_len = cfg80211_defragment_element(elems->ml_reconf_elem, 581862306a36Sopenharmony_ci elems->ie_start, 581962306a36Sopenharmony_ci elems->total_len, 582062306a36Sopenharmony_ci elems->scratch_pos, 582162306a36Sopenharmony_ci elems->scratch + elems->scratch_len - 582262306a36Sopenharmony_ci elems->scratch_pos, 582362306a36Sopenharmony_ci WLAN_EID_FRAGMENT); 582462306a36Sopenharmony_ci if (ml_len < 0) 582562306a36Sopenharmony_ci return; 582662306a36Sopenharmony_ci 582762306a36Sopenharmony_ci elems->ml_reconf = (const void *)elems->scratch_pos; 582862306a36Sopenharmony_ci elems->ml_reconf_len = ml_len; 582962306a36Sopenharmony_ci ml = elems->ml_reconf; 583062306a36Sopenharmony_ci 583162306a36Sopenharmony_ci /* Directly parse the sub elements as the common information doesn't 583262306a36Sopenharmony_ci * hold any useful information. 583362306a36Sopenharmony_ci */ 583462306a36Sopenharmony_ci for_each_mle_subelement(sub, (u8 *)ml, ml_len) { 583562306a36Sopenharmony_ci struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data; 583662306a36Sopenharmony_ci u8 *pos = prof->variable; 583762306a36Sopenharmony_ci u16 control; 583862306a36Sopenharmony_ci 583962306a36Sopenharmony_ci if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) 584062306a36Sopenharmony_ci continue; 584162306a36Sopenharmony_ci 584262306a36Sopenharmony_ci if (!ieee80211_mle_reconf_sta_prof_size_ok(sub->data, 584362306a36Sopenharmony_ci sub->datalen)) 584462306a36Sopenharmony_ci return; 584562306a36Sopenharmony_ci 584662306a36Sopenharmony_ci control = le16_to_cpu(prof->control); 584762306a36Sopenharmony_ci link_id = control & IEEE80211_MLE_STA_RECONF_CONTROL_LINK_ID; 584862306a36Sopenharmony_ci 584962306a36Sopenharmony_ci removed_links |= BIT(link_id); 585062306a36Sopenharmony_ci 585162306a36Sopenharmony_ci /* the MAC address should not be included, but handle it */ 585262306a36Sopenharmony_ci if (control & 585362306a36Sopenharmony_ci IEEE80211_MLE_STA_RECONF_CONTROL_STA_MAC_ADDR_PRESENT) 585462306a36Sopenharmony_ci pos += 6; 585562306a36Sopenharmony_ci 585662306a36Sopenharmony_ci /* According to Draft P802.11be_D3.0, the control should 585762306a36Sopenharmony_ci * include the AP Removal Timer present. If the AP Removal Timer 585862306a36Sopenharmony_ci * is not present assume immediate removal. 585962306a36Sopenharmony_ci */ 586062306a36Sopenharmony_ci if (control & 586162306a36Sopenharmony_ci IEEE80211_MLE_STA_RECONF_CONTROL_AP_REM_TIMER_PRESENT) 586262306a36Sopenharmony_ci link_removal_timeout[link_id] = le16_to_cpu(*(__le16 *)pos); 586362306a36Sopenharmony_ci } 586462306a36Sopenharmony_ci 586562306a36Sopenharmony_ci removed_links &= sdata->vif.valid_links; 586662306a36Sopenharmony_ci if (!removed_links) { 586762306a36Sopenharmony_ci /* In case the removal was cancelled, abort it */ 586862306a36Sopenharmony_ci if (sdata->u.mgd.removed_links) { 586962306a36Sopenharmony_ci sdata->u.mgd.removed_links = 0; 587062306a36Sopenharmony_ci wiphy_delayed_work_cancel(sdata->local->hw.wiphy, 587162306a36Sopenharmony_ci &sdata->u.mgd.ml_reconf_work); 587262306a36Sopenharmony_ci } 587362306a36Sopenharmony_ci return; 587462306a36Sopenharmony_ci } 587562306a36Sopenharmony_ci 587662306a36Sopenharmony_ci delay = 0; 587762306a36Sopenharmony_ci for_each_set_bit(link_id, &removed_links, IEEE80211_MLD_MAX_NUM_LINKS) { 587862306a36Sopenharmony_ci struct ieee80211_bss_conf *link_conf = 587962306a36Sopenharmony_ci sdata_dereference(sdata->vif.link_conf[link_id], sdata); 588062306a36Sopenharmony_ci u32 link_delay; 588162306a36Sopenharmony_ci 588262306a36Sopenharmony_ci if (!link_conf) { 588362306a36Sopenharmony_ci removed_links &= ~BIT(link_id); 588462306a36Sopenharmony_ci continue; 588562306a36Sopenharmony_ci } 588662306a36Sopenharmony_ci 588762306a36Sopenharmony_ci link_delay = link_conf->beacon_int * 588862306a36Sopenharmony_ci link_removal_timeout[link_id]; 588962306a36Sopenharmony_ci 589062306a36Sopenharmony_ci if (!delay) 589162306a36Sopenharmony_ci delay = link_delay; 589262306a36Sopenharmony_ci else 589362306a36Sopenharmony_ci delay = min(delay, link_delay); 589462306a36Sopenharmony_ci } 589562306a36Sopenharmony_ci 589662306a36Sopenharmony_ci sdata->u.mgd.removed_links = removed_links; 589762306a36Sopenharmony_ci wiphy_delayed_work_queue(sdata->local->hw.wiphy, 589862306a36Sopenharmony_ci &sdata->u.mgd.ml_reconf_work, 589962306a36Sopenharmony_ci TU_TO_JIFFIES(delay)); 590062306a36Sopenharmony_ci} 590162306a36Sopenharmony_ci 590262306a36Sopenharmony_cistatic void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link, 590362306a36Sopenharmony_ci struct ieee80211_hdr *hdr, size_t len, 590462306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status) 590562306a36Sopenharmony_ci{ 590662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 590762306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 590862306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; 590962306a36Sopenharmony_ci struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; 591062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *) hdr; 591162306a36Sopenharmony_ci size_t baselen; 591262306a36Sopenharmony_ci struct ieee802_11_elems *elems; 591362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 591462306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 591562306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 591662306a36Sopenharmony_ci struct ieee80211_channel *chan; 591762306a36Sopenharmony_ci struct link_sta_info *link_sta; 591862306a36Sopenharmony_ci struct sta_info *sta; 591962306a36Sopenharmony_ci u64 changed = 0; 592062306a36Sopenharmony_ci bool erp_valid; 592162306a36Sopenharmony_ci u8 erp_value = 0; 592262306a36Sopenharmony_ci u32 ncrc = 0; 592362306a36Sopenharmony_ci u8 *bssid, *variable = mgmt->u.beacon.variable; 592462306a36Sopenharmony_ci u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN]; 592562306a36Sopenharmony_ci struct ieee80211_elems_parse_params parse_params = { 592662306a36Sopenharmony_ci .link_id = -1, 592762306a36Sopenharmony_ci .from_ap = true, 592862306a36Sopenharmony_ci }; 592962306a36Sopenharmony_ci 593062306a36Sopenharmony_ci sdata_assert_lock(sdata); 593162306a36Sopenharmony_ci 593262306a36Sopenharmony_ci /* Process beacon from the current BSS */ 593362306a36Sopenharmony_ci bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type); 593462306a36Sopenharmony_ci if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { 593562306a36Sopenharmony_ci struct ieee80211_ext *ext = (void *) mgmt; 593662306a36Sopenharmony_ci 593762306a36Sopenharmony_ci if (ieee80211_is_s1g_short_beacon(ext->frame_control)) 593862306a36Sopenharmony_ci variable = ext->u.s1g_short_beacon.variable; 593962306a36Sopenharmony_ci else 594062306a36Sopenharmony_ci variable = ext->u.s1g_beacon.variable; 594162306a36Sopenharmony_ci } 594262306a36Sopenharmony_ci 594362306a36Sopenharmony_ci baselen = (u8 *) variable - (u8 *) mgmt; 594462306a36Sopenharmony_ci if (baselen > len) 594562306a36Sopenharmony_ci return; 594662306a36Sopenharmony_ci 594762306a36Sopenharmony_ci parse_params.start = variable; 594862306a36Sopenharmony_ci parse_params.len = len - baselen; 594962306a36Sopenharmony_ci 595062306a36Sopenharmony_ci rcu_read_lock(); 595162306a36Sopenharmony_ci chanctx_conf = rcu_dereference(link->conf->chanctx_conf); 595262306a36Sopenharmony_ci if (!chanctx_conf) { 595362306a36Sopenharmony_ci rcu_read_unlock(); 595462306a36Sopenharmony_ci return; 595562306a36Sopenharmony_ci } 595662306a36Sopenharmony_ci 595762306a36Sopenharmony_ci if (ieee80211_rx_status_to_khz(rx_status) != 595862306a36Sopenharmony_ci ieee80211_channel_to_khz(chanctx_conf->def.chan)) { 595962306a36Sopenharmony_ci rcu_read_unlock(); 596062306a36Sopenharmony_ci return; 596162306a36Sopenharmony_ci } 596262306a36Sopenharmony_ci chan = chanctx_conf->def.chan; 596362306a36Sopenharmony_ci rcu_read_unlock(); 596462306a36Sopenharmony_ci 596562306a36Sopenharmony_ci if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon && 596662306a36Sopenharmony_ci !WARN_ON(ieee80211_vif_is_mld(&sdata->vif)) && 596762306a36Sopenharmony_ci ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->link[0].bss)) { 596862306a36Sopenharmony_ci parse_params.bss = ifmgd->assoc_data->link[0].bss; 596962306a36Sopenharmony_ci elems = ieee802_11_parse_elems_full(&parse_params); 597062306a36Sopenharmony_ci if (!elems) 597162306a36Sopenharmony_ci return; 597262306a36Sopenharmony_ci 597362306a36Sopenharmony_ci ieee80211_rx_bss_info(link, mgmt, len, rx_status); 597462306a36Sopenharmony_ci 597562306a36Sopenharmony_ci if (elems->dtim_period) 597662306a36Sopenharmony_ci link->u.mgd.dtim_period = elems->dtim_period; 597762306a36Sopenharmony_ci link->u.mgd.have_beacon = true; 597862306a36Sopenharmony_ci ifmgd->assoc_data->need_beacon = false; 597962306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { 598062306a36Sopenharmony_ci link->conf->sync_tsf = 598162306a36Sopenharmony_ci le64_to_cpu(mgmt->u.beacon.timestamp); 598262306a36Sopenharmony_ci link->conf->sync_device_ts = 598362306a36Sopenharmony_ci rx_status->device_timestamp; 598462306a36Sopenharmony_ci link->conf->sync_dtim_count = elems->dtim_count; 598562306a36Sopenharmony_ci } 598662306a36Sopenharmony_ci 598762306a36Sopenharmony_ci if (elems->mbssid_config_ie) 598862306a36Sopenharmony_ci bss_conf->profile_periodicity = 598962306a36Sopenharmony_ci elems->mbssid_config_ie->profile_periodicity; 599062306a36Sopenharmony_ci else 599162306a36Sopenharmony_ci bss_conf->profile_periodicity = 0; 599262306a36Sopenharmony_ci 599362306a36Sopenharmony_ci if (elems->ext_capab_len >= 11 && 599462306a36Sopenharmony_ci (elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) 599562306a36Sopenharmony_ci bss_conf->ema_ap = true; 599662306a36Sopenharmony_ci else 599762306a36Sopenharmony_ci bss_conf->ema_ap = false; 599862306a36Sopenharmony_ci 599962306a36Sopenharmony_ci /* continue assoc process */ 600062306a36Sopenharmony_ci ifmgd->assoc_data->timeout = jiffies; 600162306a36Sopenharmony_ci ifmgd->assoc_data->timeout_started = true; 600262306a36Sopenharmony_ci run_again(sdata, ifmgd->assoc_data->timeout); 600362306a36Sopenharmony_ci kfree(elems); 600462306a36Sopenharmony_ci return; 600562306a36Sopenharmony_ci } 600662306a36Sopenharmony_ci 600762306a36Sopenharmony_ci if (!ifmgd->associated || 600862306a36Sopenharmony_ci !ieee80211_rx_our_beacon(bssid, link->u.mgd.bss)) 600962306a36Sopenharmony_ci return; 601062306a36Sopenharmony_ci bssid = link->u.mgd.bssid; 601162306a36Sopenharmony_ci 601262306a36Sopenharmony_ci if (!(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL)) 601362306a36Sopenharmony_ci ieee80211_handle_beacon_sig(link, ifmgd, bss_conf, 601462306a36Sopenharmony_ci local, rx_status); 601562306a36Sopenharmony_ci 601662306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) { 601762306a36Sopenharmony_ci mlme_dbg_ratelimited(sdata, 601862306a36Sopenharmony_ci "cancelling AP probe due to a received beacon\n"); 601962306a36Sopenharmony_ci ieee80211_reset_ap_probe(sdata); 602062306a36Sopenharmony_ci } 602162306a36Sopenharmony_ci 602262306a36Sopenharmony_ci /* 602362306a36Sopenharmony_ci * Push the beacon loss detection into the future since 602462306a36Sopenharmony_ci * we are processing a beacon from the AP just now. 602562306a36Sopenharmony_ci */ 602662306a36Sopenharmony_ci ieee80211_sta_reset_beacon_monitor(sdata); 602762306a36Sopenharmony_ci 602862306a36Sopenharmony_ci /* TODO: CRC urrently not calculated on S1G Beacon Compatibility 602962306a36Sopenharmony_ci * element (which carries the beacon interval). Don't forget to add a 603062306a36Sopenharmony_ci * bit to care_about_ies[] above if mac80211 is interested in a 603162306a36Sopenharmony_ci * changing S1G element. 603262306a36Sopenharmony_ci */ 603362306a36Sopenharmony_ci if (!ieee80211_is_s1g_beacon(hdr->frame_control)) 603462306a36Sopenharmony_ci ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4); 603562306a36Sopenharmony_ci parse_params.bss = link->u.mgd.bss; 603662306a36Sopenharmony_ci parse_params.filter = care_about_ies; 603762306a36Sopenharmony_ci parse_params.crc = ncrc; 603862306a36Sopenharmony_ci elems = ieee802_11_parse_elems_full(&parse_params); 603962306a36Sopenharmony_ci if (!elems) 604062306a36Sopenharmony_ci return; 604162306a36Sopenharmony_ci ncrc = elems->crc; 604262306a36Sopenharmony_ci 604362306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) && 604462306a36Sopenharmony_ci ieee80211_check_tim(elems->tim, elems->tim_len, vif_cfg->aid)) { 604562306a36Sopenharmony_ci if (local->hw.conf.dynamic_ps_timeout > 0) { 604662306a36Sopenharmony_ci if (local->hw.conf.flags & IEEE80211_CONF_PS) { 604762306a36Sopenharmony_ci local->hw.conf.flags &= ~IEEE80211_CONF_PS; 604862306a36Sopenharmony_ci ieee80211_hw_config(local, 604962306a36Sopenharmony_ci IEEE80211_CONF_CHANGE_PS); 605062306a36Sopenharmony_ci } 605162306a36Sopenharmony_ci ieee80211_send_nullfunc(local, sdata, false); 605262306a36Sopenharmony_ci } else if (!local->pspolling && sdata->u.mgd.powersave) { 605362306a36Sopenharmony_ci local->pspolling = true; 605462306a36Sopenharmony_ci 605562306a36Sopenharmony_ci /* 605662306a36Sopenharmony_ci * Here is assumed that the driver will be 605762306a36Sopenharmony_ci * able to send ps-poll frame and receive a 605862306a36Sopenharmony_ci * response even though power save mode is 605962306a36Sopenharmony_ci * enabled, but some drivers might require 606062306a36Sopenharmony_ci * to disable power save here. This needs 606162306a36Sopenharmony_ci * to be investigated. 606262306a36Sopenharmony_ci */ 606362306a36Sopenharmony_ci ieee80211_send_pspoll(local, sdata); 606462306a36Sopenharmony_ci } 606562306a36Sopenharmony_ci } 606662306a36Sopenharmony_ci 606762306a36Sopenharmony_ci if (sdata->vif.p2p || 606862306a36Sopenharmony_ci sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) { 606962306a36Sopenharmony_ci struct ieee80211_p2p_noa_attr noa = {}; 607062306a36Sopenharmony_ci int ret; 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci ret = cfg80211_get_p2p_attr(variable, 607362306a36Sopenharmony_ci len - baselen, 607462306a36Sopenharmony_ci IEEE80211_P2P_ATTR_ABSENCE_NOTICE, 607562306a36Sopenharmony_ci (u8 *) &noa, sizeof(noa)); 607662306a36Sopenharmony_ci if (ret >= 2) { 607762306a36Sopenharmony_ci if (link->u.mgd.p2p_noa_index != noa.index) { 607862306a36Sopenharmony_ci /* valid noa_attr and index changed */ 607962306a36Sopenharmony_ci link->u.mgd.p2p_noa_index = noa.index; 608062306a36Sopenharmony_ci memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa)); 608162306a36Sopenharmony_ci changed |= BSS_CHANGED_P2P_PS; 608262306a36Sopenharmony_ci /* 608362306a36Sopenharmony_ci * make sure we update all information, the CRC 608462306a36Sopenharmony_ci * mechanism doesn't look at P2P attributes. 608562306a36Sopenharmony_ci */ 608662306a36Sopenharmony_ci link->u.mgd.beacon_crc_valid = false; 608762306a36Sopenharmony_ci } 608862306a36Sopenharmony_ci } else if (link->u.mgd.p2p_noa_index != -1) { 608962306a36Sopenharmony_ci /* noa_attr not found and we had valid noa_attr before */ 609062306a36Sopenharmony_ci link->u.mgd.p2p_noa_index = -1; 609162306a36Sopenharmony_ci memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr)); 609262306a36Sopenharmony_ci changed |= BSS_CHANGED_P2P_PS; 609362306a36Sopenharmony_ci link->u.mgd.beacon_crc_valid = false; 609462306a36Sopenharmony_ci } 609562306a36Sopenharmony_ci } 609662306a36Sopenharmony_ci 609762306a36Sopenharmony_ci if (link->u.mgd.csa_waiting_bcn) 609862306a36Sopenharmony_ci ieee80211_chswitch_post_beacon(link); 609962306a36Sopenharmony_ci 610062306a36Sopenharmony_ci /* 610162306a36Sopenharmony_ci * Update beacon timing and dtim count on every beacon appearance. This 610262306a36Sopenharmony_ci * will allow the driver to use the most updated values. Do it before 610362306a36Sopenharmony_ci * comparing this one with last received beacon. 610462306a36Sopenharmony_ci * IMPORTANT: These parameters would possibly be out of sync by the time 610562306a36Sopenharmony_ci * the driver will use them. The synchronized view is currently 610662306a36Sopenharmony_ci * guaranteed only in certain callbacks. 610762306a36Sopenharmony_ci */ 610862306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY) && 610962306a36Sopenharmony_ci !ieee80211_is_s1g_beacon(hdr->frame_control)) { 611062306a36Sopenharmony_ci link->conf->sync_tsf = 611162306a36Sopenharmony_ci le64_to_cpu(mgmt->u.beacon.timestamp); 611262306a36Sopenharmony_ci link->conf->sync_device_ts = 611362306a36Sopenharmony_ci rx_status->device_timestamp; 611462306a36Sopenharmony_ci link->conf->sync_dtim_count = elems->dtim_count; 611562306a36Sopenharmony_ci } 611662306a36Sopenharmony_ci 611762306a36Sopenharmony_ci if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) || 611862306a36Sopenharmony_ci ieee80211_is_s1g_short_beacon(mgmt->frame_control)) 611962306a36Sopenharmony_ci goto free; 612062306a36Sopenharmony_ci link->u.mgd.beacon_crc = ncrc; 612162306a36Sopenharmony_ci link->u.mgd.beacon_crc_valid = true; 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci ieee80211_rx_bss_info(link, mgmt, len, rx_status); 612462306a36Sopenharmony_ci 612562306a36Sopenharmony_ci ieee80211_sta_process_chanswitch(link, rx_status->mactime, 612662306a36Sopenharmony_ci rx_status->device_timestamp, 612762306a36Sopenharmony_ci elems, true); 612862306a36Sopenharmony_ci 612962306a36Sopenharmony_ci if (!link->u.mgd.disable_wmm_tracking && 613062306a36Sopenharmony_ci ieee80211_sta_wmm_params(local, link, elems->wmm_param, 613162306a36Sopenharmony_ci elems->wmm_param_len, 613262306a36Sopenharmony_ci elems->mu_edca_param_set)) 613362306a36Sopenharmony_ci changed |= BSS_CHANGED_QOS; 613462306a36Sopenharmony_ci 613562306a36Sopenharmony_ci /* 613662306a36Sopenharmony_ci * If we haven't had a beacon before, tell the driver about the 613762306a36Sopenharmony_ci * DTIM period (and beacon timing if desired) now. 613862306a36Sopenharmony_ci */ 613962306a36Sopenharmony_ci if (!link->u.mgd.have_beacon) { 614062306a36Sopenharmony_ci /* a few bogus AP send dtim_period = 0 or no TIM IE */ 614162306a36Sopenharmony_ci bss_conf->dtim_period = elems->dtim_period ?: 1; 614262306a36Sopenharmony_ci 614362306a36Sopenharmony_ci changed |= BSS_CHANGED_BEACON_INFO; 614462306a36Sopenharmony_ci link->u.mgd.have_beacon = true; 614562306a36Sopenharmony_ci 614662306a36Sopenharmony_ci mutex_lock(&local->iflist_mtx); 614762306a36Sopenharmony_ci ieee80211_recalc_ps(local); 614862306a36Sopenharmony_ci mutex_unlock(&local->iflist_mtx); 614962306a36Sopenharmony_ci 615062306a36Sopenharmony_ci ieee80211_recalc_ps_vif(sdata); 615162306a36Sopenharmony_ci } 615262306a36Sopenharmony_ci 615362306a36Sopenharmony_ci if (elems->erp_info) { 615462306a36Sopenharmony_ci erp_valid = true; 615562306a36Sopenharmony_ci erp_value = elems->erp_info[0]; 615662306a36Sopenharmony_ci } else { 615762306a36Sopenharmony_ci erp_valid = false; 615862306a36Sopenharmony_ci } 615962306a36Sopenharmony_ci 616062306a36Sopenharmony_ci if (!ieee80211_is_s1g_beacon(hdr->frame_control)) 616162306a36Sopenharmony_ci changed |= ieee80211_handle_bss_capability(link, 616262306a36Sopenharmony_ci le16_to_cpu(mgmt->u.beacon.capab_info), 616362306a36Sopenharmony_ci erp_valid, erp_value); 616462306a36Sopenharmony_ci 616562306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 616662306a36Sopenharmony_ci sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); 616762306a36Sopenharmony_ci if (WARN_ON(!sta)) { 616862306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 616962306a36Sopenharmony_ci goto free; 617062306a36Sopenharmony_ci } 617162306a36Sopenharmony_ci link_sta = rcu_dereference_protected(sta->link[link->link_id], 617262306a36Sopenharmony_ci lockdep_is_held(&local->sta_mtx)); 617362306a36Sopenharmony_ci if (WARN_ON(!link_sta)) { 617462306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 617562306a36Sopenharmony_ci goto free; 617662306a36Sopenharmony_ci } 617762306a36Sopenharmony_ci 617862306a36Sopenharmony_ci if (WARN_ON(!link->conf->chandef.chan)) 617962306a36Sopenharmony_ci goto free; 618062306a36Sopenharmony_ci 618162306a36Sopenharmony_ci sband = local->hw.wiphy->bands[link->conf->chandef.chan->band]; 618262306a36Sopenharmony_ci 618362306a36Sopenharmony_ci changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems); 618462306a36Sopenharmony_ci 618562306a36Sopenharmony_ci if (ieee80211_config_bw(link, elems->ht_cap_elem, 618662306a36Sopenharmony_ci elems->vht_cap_elem, elems->ht_operation, 618762306a36Sopenharmony_ci elems->vht_operation, elems->he_operation, 618862306a36Sopenharmony_ci elems->eht_operation, 618962306a36Sopenharmony_ci elems->s1g_oper, bssid, &changed)) { 619062306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 619162306a36Sopenharmony_ci sdata_info(sdata, 619262306a36Sopenharmony_ci "failed to follow AP %pM bandwidth change, disconnect\n", 619362306a36Sopenharmony_ci bssid); 619462306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, 619562306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 619662306a36Sopenharmony_ci true, deauth_buf); 619762306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, deauth_buf, 619862306a36Sopenharmony_ci sizeof(deauth_buf), true, 619962306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 620062306a36Sopenharmony_ci false); 620162306a36Sopenharmony_ci goto free; 620262306a36Sopenharmony_ci } 620362306a36Sopenharmony_ci 620462306a36Sopenharmony_ci if (elems->opmode_notif) 620562306a36Sopenharmony_ci ieee80211_vht_handle_opmode(sdata, link_sta, 620662306a36Sopenharmony_ci *elems->opmode_notif, 620762306a36Sopenharmony_ci rx_status->band); 620862306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 620962306a36Sopenharmony_ci 621062306a36Sopenharmony_ci changed |= ieee80211_handle_pwr_constr(link, chan, mgmt, 621162306a36Sopenharmony_ci elems->country_elem, 621262306a36Sopenharmony_ci elems->country_elem_len, 621362306a36Sopenharmony_ci elems->pwr_constr_elem, 621462306a36Sopenharmony_ci elems->cisco_dtpc_elem); 621562306a36Sopenharmony_ci 621662306a36Sopenharmony_ci if (elems->eht_operation && 621762306a36Sopenharmony_ci !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) { 621862306a36Sopenharmony_ci if (!ieee80211_config_puncturing(link, elems->eht_operation, 621962306a36Sopenharmony_ci &changed)) { 622062306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, 622162306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 622262306a36Sopenharmony_ci true, deauth_buf); 622362306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, deauth_buf, 622462306a36Sopenharmony_ci sizeof(deauth_buf), true, 622562306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 622662306a36Sopenharmony_ci false); 622762306a36Sopenharmony_ci goto free; 622862306a36Sopenharmony_ci } 622962306a36Sopenharmony_ci } 623062306a36Sopenharmony_ci 623162306a36Sopenharmony_ci ieee80211_ml_reconfiguration(sdata, elems); 623262306a36Sopenharmony_ci 623362306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, changed); 623462306a36Sopenharmony_cifree: 623562306a36Sopenharmony_ci kfree(elems); 623662306a36Sopenharmony_ci} 623762306a36Sopenharmony_ci 623862306a36Sopenharmony_civoid ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata, 623962306a36Sopenharmony_ci struct sk_buff *skb) 624062306a36Sopenharmony_ci{ 624162306a36Sopenharmony_ci struct ieee80211_link_data *link = &sdata->deflink; 624262306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status; 624362306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 624462306a36Sopenharmony_ci u16 fc; 624562306a36Sopenharmony_ci 624662306a36Sopenharmony_ci rx_status = (struct ieee80211_rx_status *) skb->cb; 624762306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *) skb->data; 624862306a36Sopenharmony_ci fc = le16_to_cpu(hdr->frame_control); 624962306a36Sopenharmony_ci 625062306a36Sopenharmony_ci sdata_lock(sdata); 625162306a36Sopenharmony_ci switch (fc & IEEE80211_FCTL_STYPE) { 625262306a36Sopenharmony_ci case IEEE80211_STYPE_S1G_BEACON: 625362306a36Sopenharmony_ci ieee80211_rx_mgmt_beacon(link, hdr, skb->len, rx_status); 625462306a36Sopenharmony_ci break; 625562306a36Sopenharmony_ci } 625662306a36Sopenharmony_ci sdata_unlock(sdata); 625762306a36Sopenharmony_ci} 625862306a36Sopenharmony_ci 625962306a36Sopenharmony_civoid ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, 626062306a36Sopenharmony_ci struct sk_buff *skb) 626162306a36Sopenharmony_ci{ 626262306a36Sopenharmony_ci struct ieee80211_link_data *link = &sdata->deflink; 626362306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status; 626462306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 626562306a36Sopenharmony_ci u16 fc; 626662306a36Sopenharmony_ci int ies_len; 626762306a36Sopenharmony_ci 626862306a36Sopenharmony_ci rx_status = (struct ieee80211_rx_status *) skb->cb; 626962306a36Sopenharmony_ci mgmt = (struct ieee80211_mgmt *) skb->data; 627062306a36Sopenharmony_ci fc = le16_to_cpu(mgmt->frame_control); 627162306a36Sopenharmony_ci 627262306a36Sopenharmony_ci sdata_lock(sdata); 627362306a36Sopenharmony_ci 627462306a36Sopenharmony_ci if (rx_status->link_valid) { 627562306a36Sopenharmony_ci link = sdata_dereference(sdata->link[rx_status->link_id], 627662306a36Sopenharmony_ci sdata); 627762306a36Sopenharmony_ci if (!link) 627862306a36Sopenharmony_ci goto out; 627962306a36Sopenharmony_ci } 628062306a36Sopenharmony_ci 628162306a36Sopenharmony_ci switch (fc & IEEE80211_FCTL_STYPE) { 628262306a36Sopenharmony_ci case IEEE80211_STYPE_BEACON: 628362306a36Sopenharmony_ci ieee80211_rx_mgmt_beacon(link, (void *)mgmt, 628462306a36Sopenharmony_ci skb->len, rx_status); 628562306a36Sopenharmony_ci break; 628662306a36Sopenharmony_ci case IEEE80211_STYPE_PROBE_RESP: 628762306a36Sopenharmony_ci ieee80211_rx_mgmt_probe_resp(link, skb); 628862306a36Sopenharmony_ci break; 628962306a36Sopenharmony_ci case IEEE80211_STYPE_AUTH: 629062306a36Sopenharmony_ci ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len); 629162306a36Sopenharmony_ci break; 629262306a36Sopenharmony_ci case IEEE80211_STYPE_DEAUTH: 629362306a36Sopenharmony_ci ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len); 629462306a36Sopenharmony_ci break; 629562306a36Sopenharmony_ci case IEEE80211_STYPE_DISASSOC: 629662306a36Sopenharmony_ci ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len); 629762306a36Sopenharmony_ci break; 629862306a36Sopenharmony_ci case IEEE80211_STYPE_ASSOC_RESP: 629962306a36Sopenharmony_ci case IEEE80211_STYPE_REASSOC_RESP: 630062306a36Sopenharmony_ci ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len); 630162306a36Sopenharmony_ci break; 630262306a36Sopenharmony_ci case IEEE80211_STYPE_ACTION: 630362306a36Sopenharmony_ci if (!sdata->u.mgd.associated || 630462306a36Sopenharmony_ci !ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) 630562306a36Sopenharmony_ci break; 630662306a36Sopenharmony_ci 630762306a36Sopenharmony_ci if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) { 630862306a36Sopenharmony_ci struct ieee802_11_elems *elems; 630962306a36Sopenharmony_ci 631062306a36Sopenharmony_ci ies_len = skb->len - 631162306a36Sopenharmony_ci offsetof(struct ieee80211_mgmt, 631262306a36Sopenharmony_ci u.action.u.chan_switch.variable); 631362306a36Sopenharmony_ci 631462306a36Sopenharmony_ci if (ies_len < 0) 631562306a36Sopenharmony_ci break; 631662306a36Sopenharmony_ci 631762306a36Sopenharmony_ci /* CSA IE cannot be overridden, no need for BSSID */ 631862306a36Sopenharmony_ci elems = ieee802_11_parse_elems( 631962306a36Sopenharmony_ci mgmt->u.action.u.chan_switch.variable, 632062306a36Sopenharmony_ci ies_len, true, NULL); 632162306a36Sopenharmony_ci 632262306a36Sopenharmony_ci if (elems && !elems->parse_error) 632362306a36Sopenharmony_ci ieee80211_sta_process_chanswitch(link, 632462306a36Sopenharmony_ci rx_status->mactime, 632562306a36Sopenharmony_ci rx_status->device_timestamp, 632662306a36Sopenharmony_ci elems, false); 632762306a36Sopenharmony_ci kfree(elems); 632862306a36Sopenharmony_ci } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { 632962306a36Sopenharmony_ci struct ieee802_11_elems *elems; 633062306a36Sopenharmony_ci 633162306a36Sopenharmony_ci ies_len = skb->len - 633262306a36Sopenharmony_ci offsetof(struct ieee80211_mgmt, 633362306a36Sopenharmony_ci u.action.u.ext_chan_switch.variable); 633462306a36Sopenharmony_ci 633562306a36Sopenharmony_ci if (ies_len < 0) 633662306a36Sopenharmony_ci break; 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci /* 633962306a36Sopenharmony_ci * extended CSA IE can't be overridden, no need for 634062306a36Sopenharmony_ci * BSSID 634162306a36Sopenharmony_ci */ 634262306a36Sopenharmony_ci elems = ieee802_11_parse_elems( 634362306a36Sopenharmony_ci mgmt->u.action.u.ext_chan_switch.variable, 634462306a36Sopenharmony_ci ies_len, true, NULL); 634562306a36Sopenharmony_ci 634662306a36Sopenharmony_ci if (elems && !elems->parse_error) { 634762306a36Sopenharmony_ci /* for the handling code pretend it was an IE */ 634862306a36Sopenharmony_ci elems->ext_chansw_ie = 634962306a36Sopenharmony_ci &mgmt->u.action.u.ext_chan_switch.data; 635062306a36Sopenharmony_ci 635162306a36Sopenharmony_ci ieee80211_sta_process_chanswitch(link, 635262306a36Sopenharmony_ci rx_status->mactime, 635362306a36Sopenharmony_ci rx_status->device_timestamp, 635462306a36Sopenharmony_ci elems, false); 635562306a36Sopenharmony_ci } 635662306a36Sopenharmony_ci 635762306a36Sopenharmony_ci kfree(elems); 635862306a36Sopenharmony_ci } 635962306a36Sopenharmony_ci break; 636062306a36Sopenharmony_ci } 636162306a36Sopenharmony_ciout: 636262306a36Sopenharmony_ci sdata_unlock(sdata); 636362306a36Sopenharmony_ci} 636462306a36Sopenharmony_ci 636562306a36Sopenharmony_cistatic void ieee80211_sta_timer(struct timer_list *t) 636662306a36Sopenharmony_ci{ 636762306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 636862306a36Sopenharmony_ci from_timer(sdata, t, u.mgd.timer); 636962306a36Sopenharmony_ci 637062306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work); 637162306a36Sopenharmony_ci} 637262306a36Sopenharmony_ci 637362306a36Sopenharmony_civoid ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, 637462306a36Sopenharmony_ci u8 reason, bool tx) 637562306a36Sopenharmony_ci{ 637662306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 637762306a36Sopenharmony_ci 637862306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, 637962306a36Sopenharmony_ci tx, frame_buf); 638062306a36Sopenharmony_ci 638162306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, 638262306a36Sopenharmony_ci reason, false); 638362306a36Sopenharmony_ci} 638462306a36Sopenharmony_ci 638562306a36Sopenharmony_cistatic int ieee80211_auth(struct ieee80211_sub_if_data *sdata) 638662306a36Sopenharmony_ci{ 638762306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 638862306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 638962306a36Sopenharmony_ci struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data; 639062306a36Sopenharmony_ci u32 tx_flags = 0; 639162306a36Sopenharmony_ci u16 trans = 1; 639262306a36Sopenharmony_ci u16 status = 0; 639362306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = { 639462306a36Sopenharmony_ci .subtype = IEEE80211_STYPE_AUTH, 639562306a36Sopenharmony_ci }; 639662306a36Sopenharmony_ci 639762306a36Sopenharmony_ci sdata_assert_lock(sdata); 639862306a36Sopenharmony_ci 639962306a36Sopenharmony_ci if (WARN_ON_ONCE(!auth_data)) 640062306a36Sopenharmony_ci return -EINVAL; 640162306a36Sopenharmony_ci 640262306a36Sopenharmony_ci auth_data->tries++; 640362306a36Sopenharmony_ci 640462306a36Sopenharmony_ci if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) { 640562306a36Sopenharmony_ci sdata_info(sdata, "authentication with %pM timed out\n", 640662306a36Sopenharmony_ci auth_data->ap_addr); 640762306a36Sopenharmony_ci 640862306a36Sopenharmony_ci /* 640962306a36Sopenharmony_ci * Most likely AP is not in the range so remove the 641062306a36Sopenharmony_ci * bss struct for that AP. 641162306a36Sopenharmony_ci */ 641262306a36Sopenharmony_ci cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss); 641362306a36Sopenharmony_ci 641462306a36Sopenharmony_ci return -ETIMEDOUT; 641562306a36Sopenharmony_ci } 641662306a36Sopenharmony_ci 641762306a36Sopenharmony_ci if (auth_data->algorithm == WLAN_AUTH_SAE) 641862306a36Sopenharmony_ci info.duration = jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE); 641962306a36Sopenharmony_ci 642062306a36Sopenharmony_ci drv_mgd_prepare_tx(local, sdata, &info); 642162306a36Sopenharmony_ci 642262306a36Sopenharmony_ci sdata_info(sdata, "send auth to %pM (try %d/%d)\n", 642362306a36Sopenharmony_ci auth_data->ap_addr, auth_data->tries, 642462306a36Sopenharmony_ci IEEE80211_AUTH_MAX_TRIES); 642562306a36Sopenharmony_ci 642662306a36Sopenharmony_ci auth_data->expected_transaction = 2; 642762306a36Sopenharmony_ci 642862306a36Sopenharmony_ci if (auth_data->algorithm == WLAN_AUTH_SAE) { 642962306a36Sopenharmony_ci trans = auth_data->sae_trans; 643062306a36Sopenharmony_ci status = auth_data->sae_status; 643162306a36Sopenharmony_ci auth_data->expected_transaction = trans; 643262306a36Sopenharmony_ci } 643362306a36Sopenharmony_ci 643462306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) 643562306a36Sopenharmony_ci tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS | 643662306a36Sopenharmony_ci IEEE80211_TX_INTFL_MLME_CONN_TX; 643762306a36Sopenharmony_ci 643862306a36Sopenharmony_ci ieee80211_send_auth(sdata, trans, auth_data->algorithm, status, 643962306a36Sopenharmony_ci auth_data->data, auth_data->data_len, 644062306a36Sopenharmony_ci auth_data->ap_addr, auth_data->ap_addr, 644162306a36Sopenharmony_ci NULL, 0, 0, tx_flags); 644262306a36Sopenharmony_ci 644362306a36Sopenharmony_ci if (tx_flags == 0) { 644462306a36Sopenharmony_ci if (auth_data->algorithm == WLAN_AUTH_SAE) 644562306a36Sopenharmony_ci auth_data->timeout = jiffies + 644662306a36Sopenharmony_ci IEEE80211_AUTH_TIMEOUT_SAE; 644762306a36Sopenharmony_ci else 644862306a36Sopenharmony_ci auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; 644962306a36Sopenharmony_ci } else { 645062306a36Sopenharmony_ci auth_data->timeout = 645162306a36Sopenharmony_ci round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG); 645262306a36Sopenharmony_ci } 645362306a36Sopenharmony_ci 645462306a36Sopenharmony_ci auth_data->timeout_started = true; 645562306a36Sopenharmony_ci run_again(sdata, auth_data->timeout); 645662306a36Sopenharmony_ci 645762306a36Sopenharmony_ci return 0; 645862306a36Sopenharmony_ci} 645962306a36Sopenharmony_ci 646062306a36Sopenharmony_cistatic int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) 646162306a36Sopenharmony_ci{ 646262306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data; 646362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 646462306a36Sopenharmony_ci int ret; 646562306a36Sopenharmony_ci 646662306a36Sopenharmony_ci sdata_assert_lock(sdata); 646762306a36Sopenharmony_ci 646862306a36Sopenharmony_ci assoc_data->tries++; 646962306a36Sopenharmony_ci if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) { 647062306a36Sopenharmony_ci sdata_info(sdata, "association with %pM timed out\n", 647162306a36Sopenharmony_ci assoc_data->ap_addr); 647262306a36Sopenharmony_ci 647362306a36Sopenharmony_ci /* 647462306a36Sopenharmony_ci * Most likely AP is not in the range so remove the 647562306a36Sopenharmony_ci * bss struct for that AP. 647662306a36Sopenharmony_ci */ 647762306a36Sopenharmony_ci cfg80211_unlink_bss(local->hw.wiphy, 647862306a36Sopenharmony_ci assoc_data->link[assoc_data->assoc_link_id].bss); 647962306a36Sopenharmony_ci 648062306a36Sopenharmony_ci return -ETIMEDOUT; 648162306a36Sopenharmony_ci } 648262306a36Sopenharmony_ci 648362306a36Sopenharmony_ci sdata_info(sdata, "associate with %pM (try %d/%d)\n", 648462306a36Sopenharmony_ci assoc_data->ap_addr, assoc_data->tries, 648562306a36Sopenharmony_ci IEEE80211_ASSOC_MAX_TRIES); 648662306a36Sopenharmony_ci ret = ieee80211_send_assoc(sdata); 648762306a36Sopenharmony_ci if (ret) 648862306a36Sopenharmony_ci return ret; 648962306a36Sopenharmony_ci 649062306a36Sopenharmony_ci if (!ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) { 649162306a36Sopenharmony_ci assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT; 649262306a36Sopenharmony_ci assoc_data->timeout_started = true; 649362306a36Sopenharmony_ci run_again(sdata, assoc_data->timeout); 649462306a36Sopenharmony_ci } else { 649562306a36Sopenharmony_ci assoc_data->timeout = 649662306a36Sopenharmony_ci round_jiffies_up(jiffies + 649762306a36Sopenharmony_ci IEEE80211_ASSOC_TIMEOUT_LONG); 649862306a36Sopenharmony_ci assoc_data->timeout_started = true; 649962306a36Sopenharmony_ci run_again(sdata, assoc_data->timeout); 650062306a36Sopenharmony_ci } 650162306a36Sopenharmony_ci 650262306a36Sopenharmony_ci return 0; 650362306a36Sopenharmony_ci} 650462306a36Sopenharmony_ci 650562306a36Sopenharmony_civoid ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, 650662306a36Sopenharmony_ci __le16 fc, bool acked) 650762306a36Sopenharmony_ci{ 650862306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 650962306a36Sopenharmony_ci 651062306a36Sopenharmony_ci sdata->u.mgd.status_fc = fc; 651162306a36Sopenharmony_ci sdata->u.mgd.status_acked = acked; 651262306a36Sopenharmony_ci sdata->u.mgd.status_received = true; 651362306a36Sopenharmony_ci 651462306a36Sopenharmony_ci wiphy_work_queue(local->hw.wiphy, &sdata->work); 651562306a36Sopenharmony_ci} 651662306a36Sopenharmony_ci 651762306a36Sopenharmony_civoid ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) 651862306a36Sopenharmony_ci{ 651962306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 652062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 652162306a36Sopenharmony_ci 652262306a36Sopenharmony_ci sdata_lock(sdata); 652362306a36Sopenharmony_ci 652462306a36Sopenharmony_ci if (ifmgd->status_received) { 652562306a36Sopenharmony_ci __le16 fc = ifmgd->status_fc; 652662306a36Sopenharmony_ci bool status_acked = ifmgd->status_acked; 652762306a36Sopenharmony_ci 652862306a36Sopenharmony_ci ifmgd->status_received = false; 652962306a36Sopenharmony_ci if (ifmgd->auth_data && ieee80211_is_auth(fc)) { 653062306a36Sopenharmony_ci if (status_acked) { 653162306a36Sopenharmony_ci if (ifmgd->auth_data->algorithm == 653262306a36Sopenharmony_ci WLAN_AUTH_SAE) 653362306a36Sopenharmony_ci ifmgd->auth_data->timeout = 653462306a36Sopenharmony_ci jiffies + 653562306a36Sopenharmony_ci IEEE80211_AUTH_TIMEOUT_SAE; 653662306a36Sopenharmony_ci else 653762306a36Sopenharmony_ci ifmgd->auth_data->timeout = 653862306a36Sopenharmony_ci jiffies + 653962306a36Sopenharmony_ci IEEE80211_AUTH_TIMEOUT_SHORT; 654062306a36Sopenharmony_ci run_again(sdata, ifmgd->auth_data->timeout); 654162306a36Sopenharmony_ci } else { 654262306a36Sopenharmony_ci ifmgd->auth_data->timeout = jiffies - 1; 654362306a36Sopenharmony_ci } 654462306a36Sopenharmony_ci ifmgd->auth_data->timeout_started = true; 654562306a36Sopenharmony_ci } else if (ifmgd->assoc_data && 654662306a36Sopenharmony_ci (ieee80211_is_assoc_req(fc) || 654762306a36Sopenharmony_ci ieee80211_is_reassoc_req(fc))) { 654862306a36Sopenharmony_ci if (status_acked) { 654962306a36Sopenharmony_ci ifmgd->assoc_data->timeout = 655062306a36Sopenharmony_ci jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT; 655162306a36Sopenharmony_ci run_again(sdata, ifmgd->assoc_data->timeout); 655262306a36Sopenharmony_ci } else { 655362306a36Sopenharmony_ci ifmgd->assoc_data->timeout = jiffies - 1; 655462306a36Sopenharmony_ci } 655562306a36Sopenharmony_ci ifmgd->assoc_data->timeout_started = true; 655662306a36Sopenharmony_ci } 655762306a36Sopenharmony_ci } 655862306a36Sopenharmony_ci 655962306a36Sopenharmony_ci if (ifmgd->auth_data && ifmgd->auth_data->timeout_started && 656062306a36Sopenharmony_ci time_after(jiffies, ifmgd->auth_data->timeout)) { 656162306a36Sopenharmony_ci if (ifmgd->auth_data->done || ifmgd->auth_data->waiting) { 656262306a36Sopenharmony_ci /* 656362306a36Sopenharmony_ci * ok ... we waited for assoc or continuation but 656462306a36Sopenharmony_ci * userspace didn't do it, so kill the auth data 656562306a36Sopenharmony_ci */ 656662306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, false); 656762306a36Sopenharmony_ci } else if (ieee80211_auth(sdata)) { 656862306a36Sopenharmony_ci u8 ap_addr[ETH_ALEN]; 656962306a36Sopenharmony_ci struct ieee80211_event event = { 657062306a36Sopenharmony_ci .type = MLME_EVENT, 657162306a36Sopenharmony_ci .u.mlme.data = AUTH_EVENT, 657262306a36Sopenharmony_ci .u.mlme.status = MLME_TIMEOUT, 657362306a36Sopenharmony_ci }; 657462306a36Sopenharmony_ci 657562306a36Sopenharmony_ci memcpy(ap_addr, ifmgd->auth_data->ap_addr, ETH_ALEN); 657662306a36Sopenharmony_ci 657762306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, false); 657862306a36Sopenharmony_ci 657962306a36Sopenharmony_ci cfg80211_auth_timeout(sdata->dev, ap_addr); 658062306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 658162306a36Sopenharmony_ci } 658262306a36Sopenharmony_ci } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) 658362306a36Sopenharmony_ci run_again(sdata, ifmgd->auth_data->timeout); 658462306a36Sopenharmony_ci 658562306a36Sopenharmony_ci if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started && 658662306a36Sopenharmony_ci time_after(jiffies, ifmgd->assoc_data->timeout)) { 658762306a36Sopenharmony_ci if ((ifmgd->assoc_data->need_beacon && 658862306a36Sopenharmony_ci !sdata->deflink.u.mgd.have_beacon) || 658962306a36Sopenharmony_ci ieee80211_do_assoc(sdata)) { 659062306a36Sopenharmony_ci struct ieee80211_event event = { 659162306a36Sopenharmony_ci .type = MLME_EVENT, 659262306a36Sopenharmony_ci .u.mlme.data = ASSOC_EVENT, 659362306a36Sopenharmony_ci .u.mlme.status = MLME_TIMEOUT, 659462306a36Sopenharmony_ci }; 659562306a36Sopenharmony_ci 659662306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); 659762306a36Sopenharmony_ci drv_event_callback(sdata->local, sdata, &event); 659862306a36Sopenharmony_ci } 659962306a36Sopenharmony_ci } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) 660062306a36Sopenharmony_ci run_again(sdata, ifmgd->assoc_data->timeout); 660162306a36Sopenharmony_ci 660262306a36Sopenharmony_ci if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL && 660362306a36Sopenharmony_ci ifmgd->associated) { 660462306a36Sopenharmony_ci u8 *bssid = sdata->deflink.u.mgd.bssid; 660562306a36Sopenharmony_ci int max_tries; 660662306a36Sopenharmony_ci 660762306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) 660862306a36Sopenharmony_ci max_tries = max_nullfunc_tries; 660962306a36Sopenharmony_ci else 661062306a36Sopenharmony_ci max_tries = max_probe_tries; 661162306a36Sopenharmony_ci 661262306a36Sopenharmony_ci /* ACK received for nullfunc probing frame */ 661362306a36Sopenharmony_ci if (!ifmgd->probe_send_count) 661462306a36Sopenharmony_ci ieee80211_reset_ap_probe(sdata); 661562306a36Sopenharmony_ci else if (ifmgd->nullfunc_failed) { 661662306a36Sopenharmony_ci if (ifmgd->probe_send_count < max_tries) { 661762306a36Sopenharmony_ci mlme_dbg(sdata, 661862306a36Sopenharmony_ci "No ack for nullfunc frame to AP %pM, try %d/%i\n", 661962306a36Sopenharmony_ci bssid, ifmgd->probe_send_count, 662062306a36Sopenharmony_ci max_tries); 662162306a36Sopenharmony_ci ieee80211_mgd_probe_ap_send(sdata); 662262306a36Sopenharmony_ci } else { 662362306a36Sopenharmony_ci mlme_dbg(sdata, 662462306a36Sopenharmony_ci "No ack for nullfunc frame to AP %pM, disconnecting.\n", 662562306a36Sopenharmony_ci bssid); 662662306a36Sopenharmony_ci ieee80211_sta_connection_lost(sdata, 662762306a36Sopenharmony_ci WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, 662862306a36Sopenharmony_ci false); 662962306a36Sopenharmony_ci } 663062306a36Sopenharmony_ci } else if (time_is_after_jiffies(ifmgd->probe_timeout)) 663162306a36Sopenharmony_ci run_again(sdata, ifmgd->probe_timeout); 663262306a36Sopenharmony_ci else if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) { 663362306a36Sopenharmony_ci mlme_dbg(sdata, 663462306a36Sopenharmony_ci "Failed to send nullfunc to AP %pM after %dms, disconnecting\n", 663562306a36Sopenharmony_ci bssid, probe_wait_ms); 663662306a36Sopenharmony_ci ieee80211_sta_connection_lost(sdata, 663762306a36Sopenharmony_ci WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); 663862306a36Sopenharmony_ci } else if (ifmgd->probe_send_count < max_tries) { 663962306a36Sopenharmony_ci mlme_dbg(sdata, 664062306a36Sopenharmony_ci "No probe response from AP %pM after %dms, try %d/%i\n", 664162306a36Sopenharmony_ci bssid, probe_wait_ms, 664262306a36Sopenharmony_ci ifmgd->probe_send_count, max_tries); 664362306a36Sopenharmony_ci ieee80211_mgd_probe_ap_send(sdata); 664462306a36Sopenharmony_ci } else { 664562306a36Sopenharmony_ci /* 664662306a36Sopenharmony_ci * We actually lost the connection ... or did we? 664762306a36Sopenharmony_ci * Let's make sure! 664862306a36Sopenharmony_ci */ 664962306a36Sopenharmony_ci mlme_dbg(sdata, 665062306a36Sopenharmony_ci "No probe response from AP %pM after %dms, disconnecting.\n", 665162306a36Sopenharmony_ci bssid, probe_wait_ms); 665262306a36Sopenharmony_ci 665362306a36Sopenharmony_ci ieee80211_sta_connection_lost(sdata, 665462306a36Sopenharmony_ci WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false); 665562306a36Sopenharmony_ci } 665662306a36Sopenharmony_ci } 665762306a36Sopenharmony_ci 665862306a36Sopenharmony_ci sdata_unlock(sdata); 665962306a36Sopenharmony_ci} 666062306a36Sopenharmony_ci 666162306a36Sopenharmony_cistatic void ieee80211_sta_bcn_mon_timer(struct timer_list *t) 666262306a36Sopenharmony_ci{ 666362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 666462306a36Sopenharmony_ci from_timer(sdata, t, u.mgd.bcn_mon_timer); 666562306a36Sopenharmony_ci 666662306a36Sopenharmony_ci if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) 666762306a36Sopenharmony_ci return; 666862306a36Sopenharmony_ci 666962306a36Sopenharmony_ci if (sdata->vif.bss_conf.csa_active && 667062306a36Sopenharmony_ci !sdata->deflink.u.mgd.csa_waiting_bcn) 667162306a36Sopenharmony_ci return; 667262306a36Sopenharmony_ci 667362306a36Sopenharmony_ci if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER) 667462306a36Sopenharmony_ci return; 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci sdata->u.mgd.connection_loss = false; 667762306a36Sopenharmony_ci wiphy_work_queue(sdata->local->hw.wiphy, 667862306a36Sopenharmony_ci &sdata->u.mgd.beacon_connection_loss_work); 667962306a36Sopenharmony_ci} 668062306a36Sopenharmony_ci 668162306a36Sopenharmony_cistatic void ieee80211_sta_conn_mon_timer(struct timer_list *t) 668262306a36Sopenharmony_ci{ 668362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 668462306a36Sopenharmony_ci from_timer(sdata, t, u.mgd.conn_mon_timer); 668562306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 668662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 668762306a36Sopenharmony_ci struct sta_info *sta; 668862306a36Sopenharmony_ci unsigned long timeout; 668962306a36Sopenharmony_ci 669062306a36Sopenharmony_ci if (WARN_ON(ieee80211_vif_is_mld(&sdata->vif))) 669162306a36Sopenharmony_ci return; 669262306a36Sopenharmony_ci 669362306a36Sopenharmony_ci if (sdata->vif.bss_conf.csa_active && 669462306a36Sopenharmony_ci !sdata->deflink.u.mgd.csa_waiting_bcn) 669562306a36Sopenharmony_ci return; 669662306a36Sopenharmony_ci 669762306a36Sopenharmony_ci sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr); 669862306a36Sopenharmony_ci if (!sta) 669962306a36Sopenharmony_ci return; 670062306a36Sopenharmony_ci 670162306a36Sopenharmony_ci timeout = sta->deflink.status_stats.last_ack; 670262306a36Sopenharmony_ci if (time_before(sta->deflink.status_stats.last_ack, sta->deflink.rx_stats.last_rx)) 670362306a36Sopenharmony_ci timeout = sta->deflink.rx_stats.last_rx; 670462306a36Sopenharmony_ci timeout += IEEE80211_CONNECTION_IDLE_TIME; 670562306a36Sopenharmony_ci 670662306a36Sopenharmony_ci /* If timeout is after now, then update timer to fire at 670762306a36Sopenharmony_ci * the later date, but do not actually probe at this time. 670862306a36Sopenharmony_ci */ 670962306a36Sopenharmony_ci if (time_is_after_jiffies(timeout)) { 671062306a36Sopenharmony_ci mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout)); 671162306a36Sopenharmony_ci return; 671262306a36Sopenharmony_ci } 671362306a36Sopenharmony_ci 671462306a36Sopenharmony_ci ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); 671562306a36Sopenharmony_ci} 671662306a36Sopenharmony_ci 671762306a36Sopenharmony_cistatic void ieee80211_sta_monitor_work(struct work_struct *work) 671862306a36Sopenharmony_ci{ 671962306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = 672062306a36Sopenharmony_ci container_of(work, struct ieee80211_sub_if_data, 672162306a36Sopenharmony_ci u.mgd.monitor_work); 672262306a36Sopenharmony_ci 672362306a36Sopenharmony_ci ieee80211_mgd_probe_ap(sdata, false); 672462306a36Sopenharmony_ci} 672562306a36Sopenharmony_ci 672662306a36Sopenharmony_cistatic void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) 672762306a36Sopenharmony_ci{ 672862306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) { 672962306a36Sopenharmony_ci __ieee80211_stop_poll(sdata); 673062306a36Sopenharmony_ci 673162306a36Sopenharmony_ci /* let's probe the connection once */ 673262306a36Sopenharmony_ci if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR)) 673362306a36Sopenharmony_ci ieee80211_queue_work(&sdata->local->hw, 673462306a36Sopenharmony_ci &sdata->u.mgd.monitor_work); 673562306a36Sopenharmony_ci } 673662306a36Sopenharmony_ci} 673762306a36Sopenharmony_ci 673862306a36Sopenharmony_ci#ifdef CONFIG_PM 673962306a36Sopenharmony_civoid ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) 674062306a36Sopenharmony_ci{ 674162306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 674262306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 674362306a36Sopenharmony_ci 674462306a36Sopenharmony_ci sdata_lock(sdata); 674562306a36Sopenharmony_ci 674662306a36Sopenharmony_ci if (ifmgd->auth_data || ifmgd->assoc_data) { 674762306a36Sopenharmony_ci const u8 *ap_addr = ifmgd->auth_data ? 674862306a36Sopenharmony_ci ifmgd->auth_data->ap_addr : 674962306a36Sopenharmony_ci ifmgd->assoc_data->ap_addr; 675062306a36Sopenharmony_ci 675162306a36Sopenharmony_ci /* 675262306a36Sopenharmony_ci * If we are trying to authenticate / associate while suspending, 675362306a36Sopenharmony_ci * cfg80211 won't know and won't actually abort those attempts, 675462306a36Sopenharmony_ci * thus we need to do that ourselves. 675562306a36Sopenharmony_ci */ 675662306a36Sopenharmony_ci ieee80211_send_deauth_disassoc(sdata, ap_addr, ap_addr, 675762306a36Sopenharmony_ci IEEE80211_STYPE_DEAUTH, 675862306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 675962306a36Sopenharmony_ci false, frame_buf); 676062306a36Sopenharmony_ci if (ifmgd->assoc_data) 676162306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON); 676262306a36Sopenharmony_ci if (ifmgd->auth_data) 676362306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, false); 676462306a36Sopenharmony_ci cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, 676562306a36Sopenharmony_ci IEEE80211_DEAUTH_FRAME_LEN, 676662306a36Sopenharmony_ci false); 676762306a36Sopenharmony_ci } 676862306a36Sopenharmony_ci 676962306a36Sopenharmony_ci /* This is a bit of a hack - we should find a better and more generic 677062306a36Sopenharmony_ci * solution to this. Normally when suspending, cfg80211 will in fact 677162306a36Sopenharmony_ci * deauthenticate. However, it doesn't (and cannot) stop an ongoing 677262306a36Sopenharmony_ci * auth (not so important) or assoc (this is the problem) process. 677362306a36Sopenharmony_ci * 677462306a36Sopenharmony_ci * As a consequence, it can happen that we are in the process of both 677562306a36Sopenharmony_ci * associating and suspending, and receive an association response 677662306a36Sopenharmony_ci * after cfg80211 has checked if it needs to disconnect, but before 677762306a36Sopenharmony_ci * we actually set the flag to drop incoming frames. This will then 677862306a36Sopenharmony_ci * cause the workqueue flush to process the association response in 677962306a36Sopenharmony_ci * the suspend, resulting in a successful association just before it 678062306a36Sopenharmony_ci * tries to remove the interface from the driver, which now though 678162306a36Sopenharmony_ci * has a channel context assigned ... this results in issues. 678262306a36Sopenharmony_ci * 678362306a36Sopenharmony_ci * To work around this (for now) simply deauth here again if we're 678462306a36Sopenharmony_ci * now connected. 678562306a36Sopenharmony_ci */ 678662306a36Sopenharmony_ci if (ifmgd->associated && !sdata->local->wowlan) { 678762306a36Sopenharmony_ci u8 bssid[ETH_ALEN]; 678862306a36Sopenharmony_ci struct cfg80211_deauth_request req = { 678962306a36Sopenharmony_ci .reason_code = WLAN_REASON_DEAUTH_LEAVING, 679062306a36Sopenharmony_ci .bssid = bssid, 679162306a36Sopenharmony_ci }; 679262306a36Sopenharmony_ci 679362306a36Sopenharmony_ci memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN); 679462306a36Sopenharmony_ci ieee80211_mgd_deauth(sdata, &req); 679562306a36Sopenharmony_ci } 679662306a36Sopenharmony_ci 679762306a36Sopenharmony_ci sdata_unlock(sdata); 679862306a36Sopenharmony_ci} 679962306a36Sopenharmony_ci#endif 680062306a36Sopenharmony_ci 680162306a36Sopenharmony_civoid ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) 680262306a36Sopenharmony_ci{ 680362306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 680462306a36Sopenharmony_ci 680562306a36Sopenharmony_ci sdata_lock(sdata); 680662306a36Sopenharmony_ci if (!ifmgd->associated) { 680762306a36Sopenharmony_ci sdata_unlock(sdata); 680862306a36Sopenharmony_ci return; 680962306a36Sopenharmony_ci } 681062306a36Sopenharmony_ci 681162306a36Sopenharmony_ci if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) { 681262306a36Sopenharmony_ci sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME; 681362306a36Sopenharmony_ci mlme_dbg(sdata, "driver requested disconnect after resume\n"); 681462306a36Sopenharmony_ci ieee80211_sta_connection_lost(sdata, 681562306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, 681662306a36Sopenharmony_ci true); 681762306a36Sopenharmony_ci sdata_unlock(sdata); 681862306a36Sopenharmony_ci return; 681962306a36Sopenharmony_ci } 682062306a36Sopenharmony_ci 682162306a36Sopenharmony_ci if (sdata->flags & IEEE80211_SDATA_DISCONNECT_HW_RESTART) { 682262306a36Sopenharmony_ci sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_HW_RESTART; 682362306a36Sopenharmony_ci mlme_dbg(sdata, "driver requested disconnect after hardware restart\n"); 682462306a36Sopenharmony_ci ieee80211_sta_connection_lost(sdata, 682562306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, 682662306a36Sopenharmony_ci true); 682762306a36Sopenharmony_ci sdata_unlock(sdata); 682862306a36Sopenharmony_ci return; 682962306a36Sopenharmony_ci } 683062306a36Sopenharmony_ci 683162306a36Sopenharmony_ci sdata_unlock(sdata); 683262306a36Sopenharmony_ci} 683362306a36Sopenharmony_ci 683462306a36Sopenharmony_cistatic void ieee80211_request_smps_mgd_work(struct wiphy *wiphy, 683562306a36Sopenharmony_ci struct wiphy_work *work) 683662306a36Sopenharmony_ci{ 683762306a36Sopenharmony_ci struct ieee80211_link_data *link = 683862306a36Sopenharmony_ci container_of(work, struct ieee80211_link_data, 683962306a36Sopenharmony_ci u.mgd.request_smps_work); 684062306a36Sopenharmony_ci 684162306a36Sopenharmony_ci sdata_lock(link->sdata); 684262306a36Sopenharmony_ci __ieee80211_request_smps_mgd(link->sdata, link, 684362306a36Sopenharmony_ci link->u.mgd.driver_smps_mode); 684462306a36Sopenharmony_ci sdata_unlock(link->sdata); 684562306a36Sopenharmony_ci} 684662306a36Sopenharmony_ci 684762306a36Sopenharmony_ci/* interface setup */ 684862306a36Sopenharmony_civoid ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) 684962306a36Sopenharmony_ci{ 685062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 685162306a36Sopenharmony_ci 685262306a36Sopenharmony_ci INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); 685362306a36Sopenharmony_ci wiphy_work_init(&ifmgd->beacon_connection_loss_work, 685462306a36Sopenharmony_ci ieee80211_beacon_connection_loss_work); 685562306a36Sopenharmony_ci wiphy_work_init(&ifmgd->csa_connection_drop_work, 685662306a36Sopenharmony_ci ieee80211_csa_connection_drop_work); 685762306a36Sopenharmony_ci INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work, 685862306a36Sopenharmony_ci ieee80211_tdls_peer_del_work); 685962306a36Sopenharmony_ci wiphy_delayed_work_init(&ifmgd->ml_reconf_work, 686062306a36Sopenharmony_ci ieee80211_ml_reconf_work); 686162306a36Sopenharmony_ci timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0); 686262306a36Sopenharmony_ci timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0); 686362306a36Sopenharmony_ci timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0); 686462306a36Sopenharmony_ci INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk, 686562306a36Sopenharmony_ci ieee80211_sta_handle_tspec_ac_params_wk); 686662306a36Sopenharmony_ci 686762306a36Sopenharmony_ci ifmgd->flags = 0; 686862306a36Sopenharmony_ci ifmgd->powersave = sdata->wdev.ps; 686962306a36Sopenharmony_ci ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues; 687062306a36Sopenharmony_ci ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len; 687162306a36Sopenharmony_ci /* Setup TDLS data */ 687262306a36Sopenharmony_ci spin_lock_init(&ifmgd->teardown_lock); 687362306a36Sopenharmony_ci ifmgd->teardown_skb = NULL; 687462306a36Sopenharmony_ci ifmgd->orig_teardown_skb = NULL; 687562306a36Sopenharmony_ci} 687662306a36Sopenharmony_ci 687762306a36Sopenharmony_civoid ieee80211_mgd_setup_link(struct ieee80211_link_data *link) 687862306a36Sopenharmony_ci{ 687962306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 688062306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 688162306a36Sopenharmony_ci unsigned int link_id = link->link_id; 688262306a36Sopenharmony_ci 688362306a36Sopenharmony_ci link->u.mgd.p2p_noa_index = -1; 688462306a36Sopenharmony_ci link->u.mgd.conn_flags = 0; 688562306a36Sopenharmony_ci link->conf->bssid = link->u.mgd.bssid; 688662306a36Sopenharmony_ci 688762306a36Sopenharmony_ci wiphy_work_init(&link->u.mgd.request_smps_work, 688862306a36Sopenharmony_ci ieee80211_request_smps_mgd_work); 688962306a36Sopenharmony_ci if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS) 689062306a36Sopenharmony_ci link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC; 689162306a36Sopenharmony_ci else 689262306a36Sopenharmony_ci link->u.mgd.req_smps = IEEE80211_SMPS_OFF; 689362306a36Sopenharmony_ci 689462306a36Sopenharmony_ci wiphy_delayed_work_init(&link->u.mgd.chswitch_work, 689562306a36Sopenharmony_ci ieee80211_chswitch_work); 689662306a36Sopenharmony_ci 689762306a36Sopenharmony_ci if (sdata->u.mgd.assoc_data) 689862306a36Sopenharmony_ci ether_addr_copy(link->conf->addr, 689962306a36Sopenharmony_ci sdata->u.mgd.assoc_data->link[link_id].addr); 690062306a36Sopenharmony_ci else if (!is_valid_ether_addr(link->conf->addr)) 690162306a36Sopenharmony_ci eth_random_addr(link->conf->addr); 690262306a36Sopenharmony_ci} 690362306a36Sopenharmony_ci 690462306a36Sopenharmony_ci/* scan finished notification */ 690562306a36Sopenharmony_civoid ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) 690662306a36Sopenharmony_ci{ 690762306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 690862306a36Sopenharmony_ci 690962306a36Sopenharmony_ci /* Restart STA timers */ 691062306a36Sopenharmony_ci rcu_read_lock(); 691162306a36Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 691262306a36Sopenharmony_ci if (ieee80211_sdata_running(sdata)) 691362306a36Sopenharmony_ci ieee80211_restart_sta_timer(sdata); 691462306a36Sopenharmony_ci } 691562306a36Sopenharmony_ci rcu_read_unlock(); 691662306a36Sopenharmony_ci} 691762306a36Sopenharmony_ci 691862306a36Sopenharmony_cistatic int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, 691962306a36Sopenharmony_ci struct cfg80211_bss *cbss, s8 link_id, 692062306a36Sopenharmony_ci const u8 *ap_mld_addr, bool assoc, 692162306a36Sopenharmony_ci bool override) 692262306a36Sopenharmony_ci{ 692362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 692462306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 692562306a36Sopenharmony_ci struct ieee80211_bss *bss = (void *)cbss->priv; 692662306a36Sopenharmony_ci struct sta_info *new_sta = NULL; 692762306a36Sopenharmony_ci struct ieee80211_link_data *link; 692862306a36Sopenharmony_ci bool have_sta = false; 692962306a36Sopenharmony_ci bool mlo; 693062306a36Sopenharmony_ci int err; 693162306a36Sopenharmony_ci 693262306a36Sopenharmony_ci if (link_id >= 0) { 693362306a36Sopenharmony_ci mlo = true; 693462306a36Sopenharmony_ci if (WARN_ON(!ap_mld_addr)) 693562306a36Sopenharmony_ci return -EINVAL; 693662306a36Sopenharmony_ci err = ieee80211_vif_set_links(sdata, BIT(link_id), 0); 693762306a36Sopenharmony_ci } else { 693862306a36Sopenharmony_ci if (WARN_ON(ap_mld_addr)) 693962306a36Sopenharmony_ci return -EINVAL; 694062306a36Sopenharmony_ci ap_mld_addr = cbss->bssid; 694162306a36Sopenharmony_ci err = ieee80211_vif_set_links(sdata, 0, 0); 694262306a36Sopenharmony_ci link_id = 0; 694362306a36Sopenharmony_ci mlo = false; 694462306a36Sopenharmony_ci } 694562306a36Sopenharmony_ci 694662306a36Sopenharmony_ci if (err) 694762306a36Sopenharmony_ci return err; 694862306a36Sopenharmony_ci 694962306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 695062306a36Sopenharmony_ci if (WARN_ON(!link)) { 695162306a36Sopenharmony_ci err = -ENOLINK; 695262306a36Sopenharmony_ci goto out_err; 695362306a36Sopenharmony_ci } 695462306a36Sopenharmony_ci 695562306a36Sopenharmony_ci if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) { 695662306a36Sopenharmony_ci err = -EINVAL; 695762306a36Sopenharmony_ci goto out_err; 695862306a36Sopenharmony_ci } 695962306a36Sopenharmony_ci 696062306a36Sopenharmony_ci /* If a reconfig is happening, bail out */ 696162306a36Sopenharmony_ci if (local->in_reconfig) { 696262306a36Sopenharmony_ci err = -EBUSY; 696362306a36Sopenharmony_ci goto out_err; 696462306a36Sopenharmony_ci } 696562306a36Sopenharmony_ci 696662306a36Sopenharmony_ci if (assoc) { 696762306a36Sopenharmony_ci rcu_read_lock(); 696862306a36Sopenharmony_ci have_sta = sta_info_get(sdata, ap_mld_addr); 696962306a36Sopenharmony_ci rcu_read_unlock(); 697062306a36Sopenharmony_ci } 697162306a36Sopenharmony_ci 697262306a36Sopenharmony_ci if (!have_sta) { 697362306a36Sopenharmony_ci if (mlo) 697462306a36Sopenharmony_ci new_sta = sta_info_alloc_with_link(sdata, ap_mld_addr, 697562306a36Sopenharmony_ci link_id, cbss->bssid, 697662306a36Sopenharmony_ci GFP_KERNEL); 697762306a36Sopenharmony_ci else 697862306a36Sopenharmony_ci new_sta = sta_info_alloc(sdata, ap_mld_addr, GFP_KERNEL); 697962306a36Sopenharmony_ci 698062306a36Sopenharmony_ci if (!new_sta) { 698162306a36Sopenharmony_ci err = -ENOMEM; 698262306a36Sopenharmony_ci goto out_err; 698362306a36Sopenharmony_ci } 698462306a36Sopenharmony_ci 698562306a36Sopenharmony_ci new_sta->sta.mlo = mlo; 698662306a36Sopenharmony_ci } 698762306a36Sopenharmony_ci 698862306a36Sopenharmony_ci /* 698962306a36Sopenharmony_ci * Set up the information for the new channel before setting the 699062306a36Sopenharmony_ci * new channel. We can't - completely race-free - change the basic 699162306a36Sopenharmony_ci * rates bitmap and the channel (sband) that it refers to, but if 699262306a36Sopenharmony_ci * we set it up before we at least avoid calling into the driver's 699362306a36Sopenharmony_ci * bss_info_changed() method with invalid information (since we do 699462306a36Sopenharmony_ci * call that from changing the channel - only for IDLE and perhaps 699562306a36Sopenharmony_ci * some others, but ...). 699662306a36Sopenharmony_ci * 699762306a36Sopenharmony_ci * So to avoid that, just set up all the new information before the 699862306a36Sopenharmony_ci * channel, but tell the driver to apply it only afterwards, since 699962306a36Sopenharmony_ci * it might need the new channel for that. 700062306a36Sopenharmony_ci */ 700162306a36Sopenharmony_ci if (new_sta) { 700262306a36Sopenharmony_ci const struct cfg80211_bss_ies *ies; 700362306a36Sopenharmony_ci struct link_sta_info *link_sta; 700462306a36Sopenharmony_ci 700562306a36Sopenharmony_ci rcu_read_lock(); 700662306a36Sopenharmony_ci link_sta = rcu_dereference(new_sta->link[link_id]); 700762306a36Sopenharmony_ci if (WARN_ON(!link_sta)) { 700862306a36Sopenharmony_ci rcu_read_unlock(); 700962306a36Sopenharmony_ci sta_info_free(local, new_sta); 701062306a36Sopenharmony_ci err = -EINVAL; 701162306a36Sopenharmony_ci goto out_err; 701262306a36Sopenharmony_ci } 701362306a36Sopenharmony_ci 701462306a36Sopenharmony_ci err = ieee80211_mgd_setup_link_sta(link, new_sta, 701562306a36Sopenharmony_ci link_sta, cbss); 701662306a36Sopenharmony_ci if (err) { 701762306a36Sopenharmony_ci rcu_read_unlock(); 701862306a36Sopenharmony_ci sta_info_free(local, new_sta); 701962306a36Sopenharmony_ci goto out_err; 702062306a36Sopenharmony_ci } 702162306a36Sopenharmony_ci 702262306a36Sopenharmony_ci memcpy(link->u.mgd.bssid, cbss->bssid, ETH_ALEN); 702362306a36Sopenharmony_ci 702462306a36Sopenharmony_ci /* set timing information */ 702562306a36Sopenharmony_ci link->conf->beacon_int = cbss->beacon_interval; 702662306a36Sopenharmony_ci ies = rcu_dereference(cbss->beacon_ies); 702762306a36Sopenharmony_ci if (ies) { 702862306a36Sopenharmony_ci link->conf->sync_tsf = ies->tsf; 702962306a36Sopenharmony_ci link->conf->sync_device_ts = 703062306a36Sopenharmony_ci bss->device_ts_beacon; 703162306a36Sopenharmony_ci 703262306a36Sopenharmony_ci ieee80211_get_dtim(ies, 703362306a36Sopenharmony_ci &link->conf->sync_dtim_count, 703462306a36Sopenharmony_ci NULL); 703562306a36Sopenharmony_ci } else if (!ieee80211_hw_check(&sdata->local->hw, 703662306a36Sopenharmony_ci TIMING_BEACON_ONLY)) { 703762306a36Sopenharmony_ci ies = rcu_dereference(cbss->proberesp_ies); 703862306a36Sopenharmony_ci /* must be non-NULL since beacon IEs were NULL */ 703962306a36Sopenharmony_ci link->conf->sync_tsf = ies->tsf; 704062306a36Sopenharmony_ci link->conf->sync_device_ts = 704162306a36Sopenharmony_ci bss->device_ts_presp; 704262306a36Sopenharmony_ci link->conf->sync_dtim_count = 0; 704362306a36Sopenharmony_ci } else { 704462306a36Sopenharmony_ci link->conf->sync_tsf = 0; 704562306a36Sopenharmony_ci link->conf->sync_device_ts = 0; 704662306a36Sopenharmony_ci link->conf->sync_dtim_count = 0; 704762306a36Sopenharmony_ci } 704862306a36Sopenharmony_ci rcu_read_unlock(); 704962306a36Sopenharmony_ci } 705062306a36Sopenharmony_ci 705162306a36Sopenharmony_ci if (new_sta || override) { 705262306a36Sopenharmony_ci err = ieee80211_prep_channel(sdata, link, cbss, 705362306a36Sopenharmony_ci &link->u.mgd.conn_flags); 705462306a36Sopenharmony_ci if (err) { 705562306a36Sopenharmony_ci if (new_sta) 705662306a36Sopenharmony_ci sta_info_free(local, new_sta); 705762306a36Sopenharmony_ci goto out_err; 705862306a36Sopenharmony_ci } 705962306a36Sopenharmony_ci } 706062306a36Sopenharmony_ci 706162306a36Sopenharmony_ci if (new_sta) { 706262306a36Sopenharmony_ci /* 706362306a36Sopenharmony_ci * tell driver about BSSID, basic rates and timing 706462306a36Sopenharmony_ci * this was set up above, before setting the channel 706562306a36Sopenharmony_ci */ 706662306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, 706762306a36Sopenharmony_ci BSS_CHANGED_BSSID | 706862306a36Sopenharmony_ci BSS_CHANGED_BASIC_RATES | 706962306a36Sopenharmony_ci BSS_CHANGED_BEACON_INT); 707062306a36Sopenharmony_ci 707162306a36Sopenharmony_ci if (assoc) 707262306a36Sopenharmony_ci sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH); 707362306a36Sopenharmony_ci 707462306a36Sopenharmony_ci err = sta_info_insert(new_sta); 707562306a36Sopenharmony_ci new_sta = NULL; 707662306a36Sopenharmony_ci if (err) { 707762306a36Sopenharmony_ci sdata_info(sdata, 707862306a36Sopenharmony_ci "failed to insert STA entry for the AP (error %d)\n", 707962306a36Sopenharmony_ci err); 708062306a36Sopenharmony_ci goto out_err; 708162306a36Sopenharmony_ci } 708262306a36Sopenharmony_ci } else 708362306a36Sopenharmony_ci WARN_ON_ONCE(!ether_addr_equal(link->u.mgd.bssid, cbss->bssid)); 708462306a36Sopenharmony_ci 708562306a36Sopenharmony_ci /* Cancel scan to ensure that nothing interferes with connection */ 708662306a36Sopenharmony_ci if (local->scanning) 708762306a36Sopenharmony_ci ieee80211_scan_cancel(local); 708862306a36Sopenharmony_ci 708962306a36Sopenharmony_ci return 0; 709062306a36Sopenharmony_ci 709162306a36Sopenharmony_ciout_err: 709262306a36Sopenharmony_ci ieee80211_link_release_channel(&sdata->deflink); 709362306a36Sopenharmony_ci ieee80211_vif_set_links(sdata, 0, 0); 709462306a36Sopenharmony_ci return err; 709562306a36Sopenharmony_ci} 709662306a36Sopenharmony_ci 709762306a36Sopenharmony_ci/* config hooks */ 709862306a36Sopenharmony_ciint ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, 709962306a36Sopenharmony_ci struct cfg80211_auth_request *req) 710062306a36Sopenharmony_ci{ 710162306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 710262306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 710362306a36Sopenharmony_ci struct ieee80211_mgd_auth_data *auth_data; 710462306a36Sopenharmony_ci u16 auth_alg; 710562306a36Sopenharmony_ci int err; 710662306a36Sopenharmony_ci bool cont_auth; 710762306a36Sopenharmony_ci 710862306a36Sopenharmony_ci /* prepare auth data structure */ 710962306a36Sopenharmony_ci 711062306a36Sopenharmony_ci switch (req->auth_type) { 711162306a36Sopenharmony_ci case NL80211_AUTHTYPE_OPEN_SYSTEM: 711262306a36Sopenharmony_ci auth_alg = WLAN_AUTH_OPEN; 711362306a36Sopenharmony_ci break; 711462306a36Sopenharmony_ci case NL80211_AUTHTYPE_SHARED_KEY: 711562306a36Sopenharmony_ci if (fips_enabled) 711662306a36Sopenharmony_ci return -EOPNOTSUPP; 711762306a36Sopenharmony_ci auth_alg = WLAN_AUTH_SHARED_KEY; 711862306a36Sopenharmony_ci break; 711962306a36Sopenharmony_ci case NL80211_AUTHTYPE_FT: 712062306a36Sopenharmony_ci auth_alg = WLAN_AUTH_FT; 712162306a36Sopenharmony_ci break; 712262306a36Sopenharmony_ci case NL80211_AUTHTYPE_NETWORK_EAP: 712362306a36Sopenharmony_ci auth_alg = WLAN_AUTH_LEAP; 712462306a36Sopenharmony_ci break; 712562306a36Sopenharmony_ci case NL80211_AUTHTYPE_SAE: 712662306a36Sopenharmony_ci auth_alg = WLAN_AUTH_SAE; 712762306a36Sopenharmony_ci break; 712862306a36Sopenharmony_ci case NL80211_AUTHTYPE_FILS_SK: 712962306a36Sopenharmony_ci auth_alg = WLAN_AUTH_FILS_SK; 713062306a36Sopenharmony_ci break; 713162306a36Sopenharmony_ci case NL80211_AUTHTYPE_FILS_SK_PFS: 713262306a36Sopenharmony_ci auth_alg = WLAN_AUTH_FILS_SK_PFS; 713362306a36Sopenharmony_ci break; 713462306a36Sopenharmony_ci case NL80211_AUTHTYPE_FILS_PK: 713562306a36Sopenharmony_ci auth_alg = WLAN_AUTH_FILS_PK; 713662306a36Sopenharmony_ci break; 713762306a36Sopenharmony_ci default: 713862306a36Sopenharmony_ci return -EOPNOTSUPP; 713962306a36Sopenharmony_ci } 714062306a36Sopenharmony_ci 714162306a36Sopenharmony_ci if (ifmgd->assoc_data) 714262306a36Sopenharmony_ci return -EBUSY; 714362306a36Sopenharmony_ci 714462306a36Sopenharmony_ci auth_data = kzalloc(sizeof(*auth_data) + req->auth_data_len + 714562306a36Sopenharmony_ci req->ie_len, GFP_KERNEL); 714662306a36Sopenharmony_ci if (!auth_data) 714762306a36Sopenharmony_ci return -ENOMEM; 714862306a36Sopenharmony_ci 714962306a36Sopenharmony_ci memcpy(auth_data->ap_addr, 715062306a36Sopenharmony_ci req->ap_mld_addr ?: req->bss->bssid, 715162306a36Sopenharmony_ci ETH_ALEN); 715262306a36Sopenharmony_ci auth_data->bss = req->bss; 715362306a36Sopenharmony_ci auth_data->link_id = req->link_id; 715462306a36Sopenharmony_ci 715562306a36Sopenharmony_ci if (req->auth_data_len >= 4) { 715662306a36Sopenharmony_ci if (req->auth_type == NL80211_AUTHTYPE_SAE) { 715762306a36Sopenharmony_ci __le16 *pos = (__le16 *) req->auth_data; 715862306a36Sopenharmony_ci 715962306a36Sopenharmony_ci auth_data->sae_trans = le16_to_cpu(pos[0]); 716062306a36Sopenharmony_ci auth_data->sae_status = le16_to_cpu(pos[1]); 716162306a36Sopenharmony_ci } 716262306a36Sopenharmony_ci memcpy(auth_data->data, req->auth_data + 4, 716362306a36Sopenharmony_ci req->auth_data_len - 4); 716462306a36Sopenharmony_ci auth_data->data_len += req->auth_data_len - 4; 716562306a36Sopenharmony_ci } 716662306a36Sopenharmony_ci 716762306a36Sopenharmony_ci /* Check if continuing authentication or trying to authenticate with the 716862306a36Sopenharmony_ci * same BSS that we were in the process of authenticating with and avoid 716962306a36Sopenharmony_ci * removal and re-addition of the STA entry in 717062306a36Sopenharmony_ci * ieee80211_prep_connection(). 717162306a36Sopenharmony_ci */ 717262306a36Sopenharmony_ci cont_auth = ifmgd->auth_data && req->bss == ifmgd->auth_data->bss && 717362306a36Sopenharmony_ci ifmgd->auth_data->link_id == req->link_id; 717462306a36Sopenharmony_ci 717562306a36Sopenharmony_ci if (req->ie && req->ie_len) { 717662306a36Sopenharmony_ci memcpy(&auth_data->data[auth_data->data_len], 717762306a36Sopenharmony_ci req->ie, req->ie_len); 717862306a36Sopenharmony_ci auth_data->data_len += req->ie_len; 717962306a36Sopenharmony_ci } 718062306a36Sopenharmony_ci 718162306a36Sopenharmony_ci if (req->key && req->key_len) { 718262306a36Sopenharmony_ci auth_data->key_len = req->key_len; 718362306a36Sopenharmony_ci auth_data->key_idx = req->key_idx; 718462306a36Sopenharmony_ci memcpy(auth_data->key, req->key, req->key_len); 718562306a36Sopenharmony_ci } 718662306a36Sopenharmony_ci 718762306a36Sopenharmony_ci auth_data->algorithm = auth_alg; 718862306a36Sopenharmony_ci 718962306a36Sopenharmony_ci /* try to authenticate/probe */ 719062306a36Sopenharmony_ci 719162306a36Sopenharmony_ci if (ifmgd->auth_data) { 719262306a36Sopenharmony_ci if (cont_auth && req->auth_type == NL80211_AUTHTYPE_SAE) { 719362306a36Sopenharmony_ci auth_data->peer_confirmed = 719462306a36Sopenharmony_ci ifmgd->auth_data->peer_confirmed; 719562306a36Sopenharmony_ci } 719662306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, cont_auth); 719762306a36Sopenharmony_ci } 719862306a36Sopenharmony_ci 719962306a36Sopenharmony_ci /* prep auth_data so we don't go into idle on disassoc */ 720062306a36Sopenharmony_ci ifmgd->auth_data = auth_data; 720162306a36Sopenharmony_ci 720262306a36Sopenharmony_ci /* If this is continuation of an ongoing SAE authentication exchange 720362306a36Sopenharmony_ci * (i.e., request to send SAE Confirm) and the peer has already 720462306a36Sopenharmony_ci * confirmed, mark authentication completed since we are about to send 720562306a36Sopenharmony_ci * out SAE Confirm. 720662306a36Sopenharmony_ci */ 720762306a36Sopenharmony_ci if (cont_auth && req->auth_type == NL80211_AUTHTYPE_SAE && 720862306a36Sopenharmony_ci auth_data->peer_confirmed && auth_data->sae_trans == 2) 720962306a36Sopenharmony_ci ieee80211_mark_sta_auth(sdata); 721062306a36Sopenharmony_ci 721162306a36Sopenharmony_ci if (ifmgd->associated) { 721262306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 721362306a36Sopenharmony_ci 721462306a36Sopenharmony_ci sdata_info(sdata, 721562306a36Sopenharmony_ci "disconnect from AP %pM for new auth to %pM\n", 721662306a36Sopenharmony_ci sdata->vif.cfg.ap_addr, auth_data->ap_addr); 721762306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, 721862306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, 721962306a36Sopenharmony_ci false, frame_buf); 722062306a36Sopenharmony_ci 722162306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, 722262306a36Sopenharmony_ci sizeof(frame_buf), true, 722362306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, 722462306a36Sopenharmony_ci false); 722562306a36Sopenharmony_ci } 722662306a36Sopenharmony_ci 722762306a36Sopenharmony_ci sdata_info(sdata, "authenticate with %pM\n", auth_data->ap_addr); 722862306a36Sopenharmony_ci 722962306a36Sopenharmony_ci /* needed for transmitting the auth frame(s) properly */ 723062306a36Sopenharmony_ci memcpy(sdata->vif.cfg.ap_addr, auth_data->ap_addr, ETH_ALEN); 723162306a36Sopenharmony_ci 723262306a36Sopenharmony_ci err = ieee80211_prep_connection(sdata, req->bss, req->link_id, 723362306a36Sopenharmony_ci req->ap_mld_addr, cont_auth, false); 723462306a36Sopenharmony_ci if (err) 723562306a36Sopenharmony_ci goto err_clear; 723662306a36Sopenharmony_ci 723762306a36Sopenharmony_ci err = ieee80211_auth(sdata); 723862306a36Sopenharmony_ci if (err) { 723962306a36Sopenharmony_ci sta_info_destroy_addr(sdata, auth_data->ap_addr); 724062306a36Sopenharmony_ci goto err_clear; 724162306a36Sopenharmony_ci } 724262306a36Sopenharmony_ci 724362306a36Sopenharmony_ci /* hold our own reference */ 724462306a36Sopenharmony_ci cfg80211_ref_bss(local->hw.wiphy, auth_data->bss); 724562306a36Sopenharmony_ci return 0; 724662306a36Sopenharmony_ci 724762306a36Sopenharmony_ci err_clear: 724862306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) { 724962306a36Sopenharmony_ci eth_zero_addr(sdata->deflink.u.mgd.bssid); 725062306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, &sdata->deflink, 725162306a36Sopenharmony_ci BSS_CHANGED_BSSID); 725262306a36Sopenharmony_ci mutex_lock(&sdata->local->mtx); 725362306a36Sopenharmony_ci ieee80211_link_release_channel(&sdata->deflink); 725462306a36Sopenharmony_ci mutex_unlock(&sdata->local->mtx); 725562306a36Sopenharmony_ci } 725662306a36Sopenharmony_ci ifmgd->auth_data = NULL; 725762306a36Sopenharmony_ci kfree(auth_data); 725862306a36Sopenharmony_ci return err; 725962306a36Sopenharmony_ci} 726062306a36Sopenharmony_ci 726162306a36Sopenharmony_cistatic ieee80211_conn_flags_t 726262306a36Sopenharmony_ciieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata, 726362306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data, 726462306a36Sopenharmony_ci struct cfg80211_assoc_request *req, 726562306a36Sopenharmony_ci ieee80211_conn_flags_t conn_flags, 726662306a36Sopenharmony_ci unsigned int link_id) 726762306a36Sopenharmony_ci{ 726862306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 726962306a36Sopenharmony_ci const struct cfg80211_bss_ies *beacon_ies; 727062306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 727162306a36Sopenharmony_ci const struct element *ht_elem, *vht_elem; 727262306a36Sopenharmony_ci struct ieee80211_link_data *link; 727362306a36Sopenharmony_ci struct cfg80211_bss *cbss; 727462306a36Sopenharmony_ci struct ieee80211_bss *bss; 727562306a36Sopenharmony_ci bool is_5ghz, is_6ghz; 727662306a36Sopenharmony_ci 727762306a36Sopenharmony_ci cbss = assoc_data->link[link_id].bss; 727862306a36Sopenharmony_ci if (WARN_ON(!cbss)) 727962306a36Sopenharmony_ci return 0; 728062306a36Sopenharmony_ci 728162306a36Sopenharmony_ci bss = (void *)cbss->priv; 728262306a36Sopenharmony_ci 728362306a36Sopenharmony_ci sband = local->hw.wiphy->bands[cbss->channel->band]; 728462306a36Sopenharmony_ci if (WARN_ON(!sband)) 728562306a36Sopenharmony_ci return 0; 728662306a36Sopenharmony_ci 728762306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 728862306a36Sopenharmony_ci if (WARN_ON(!link)) 728962306a36Sopenharmony_ci return 0; 729062306a36Sopenharmony_ci 729162306a36Sopenharmony_ci is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ; 729262306a36Sopenharmony_ci is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ; 729362306a36Sopenharmony_ci 729462306a36Sopenharmony_ci /* for MLO connections assume advertising all rates is OK */ 729562306a36Sopenharmony_ci if (!req->ap_mld_addr) { 729662306a36Sopenharmony_ci assoc_data->supp_rates = bss->supp_rates; 729762306a36Sopenharmony_ci assoc_data->supp_rates_len = bss->supp_rates_len; 729862306a36Sopenharmony_ci } 729962306a36Sopenharmony_ci 730062306a36Sopenharmony_ci /* copy and link elems for the STA profile */ 730162306a36Sopenharmony_ci if (req->links[link_id].elems_len) { 730262306a36Sopenharmony_ci memcpy(assoc_data->ie_pos, req->links[link_id].elems, 730362306a36Sopenharmony_ci req->links[link_id].elems_len); 730462306a36Sopenharmony_ci assoc_data->link[link_id].elems = assoc_data->ie_pos; 730562306a36Sopenharmony_ci assoc_data->link[link_id].elems_len = req->links[link_id].elems_len; 730662306a36Sopenharmony_ci assoc_data->ie_pos += req->links[link_id].elems_len; 730762306a36Sopenharmony_ci } 730862306a36Sopenharmony_ci 730962306a36Sopenharmony_ci rcu_read_lock(); 731062306a36Sopenharmony_ci ht_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_OPERATION); 731162306a36Sopenharmony_ci if (ht_elem && ht_elem->datalen >= sizeof(struct ieee80211_ht_operation)) 731262306a36Sopenharmony_ci assoc_data->link[link_id].ap_ht_param = 731362306a36Sopenharmony_ci ((struct ieee80211_ht_operation *)(ht_elem->data))->ht_param; 731462306a36Sopenharmony_ci else if (!is_6ghz) 731562306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HT; 731662306a36Sopenharmony_ci vht_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY); 731762306a36Sopenharmony_ci if (vht_elem && vht_elem->datalen >= sizeof(struct ieee80211_vht_cap)) { 731862306a36Sopenharmony_ci memcpy(&assoc_data->link[link_id].ap_vht_cap, vht_elem->data, 731962306a36Sopenharmony_ci sizeof(struct ieee80211_vht_cap)); 732062306a36Sopenharmony_ci } else if (is_5ghz) { 732162306a36Sopenharmony_ci link_info(link, 732262306a36Sopenharmony_ci "VHT capa missing/short, disabling VHT/HE/EHT\n"); 732362306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_VHT | 732462306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_HE | 732562306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_EHT; 732662306a36Sopenharmony_ci } 732762306a36Sopenharmony_ci rcu_read_unlock(); 732862306a36Sopenharmony_ci 732962306a36Sopenharmony_ci link->u.mgd.beacon_crc_valid = false; 733062306a36Sopenharmony_ci link->u.mgd.dtim_period = 0; 733162306a36Sopenharmony_ci link->u.mgd.have_beacon = false; 733262306a36Sopenharmony_ci 733362306a36Sopenharmony_ci /* override HT/VHT configuration only if the AP and we support it */ 733462306a36Sopenharmony_ci if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) { 733562306a36Sopenharmony_ci struct ieee80211_sta_ht_cap sta_ht_cap; 733662306a36Sopenharmony_ci 733762306a36Sopenharmony_ci memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap)); 733862306a36Sopenharmony_ci ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap); 733962306a36Sopenharmony_ci } 734062306a36Sopenharmony_ci 734162306a36Sopenharmony_ci link->conf->eht_puncturing = 0; 734262306a36Sopenharmony_ci 734362306a36Sopenharmony_ci rcu_read_lock(); 734462306a36Sopenharmony_ci beacon_ies = rcu_dereference(cbss->beacon_ies); 734562306a36Sopenharmony_ci if (beacon_ies) { 734662306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper; 734762306a36Sopenharmony_ci const struct element *elem; 734862306a36Sopenharmony_ci u8 dtim_count = 0; 734962306a36Sopenharmony_ci 735062306a36Sopenharmony_ci ieee80211_get_dtim(beacon_ies, &dtim_count, 735162306a36Sopenharmony_ci &link->u.mgd.dtim_period); 735262306a36Sopenharmony_ci 735362306a36Sopenharmony_ci sdata->deflink.u.mgd.have_beacon = true; 735462306a36Sopenharmony_ci 735562306a36Sopenharmony_ci if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) { 735662306a36Sopenharmony_ci link->conf->sync_tsf = beacon_ies->tsf; 735762306a36Sopenharmony_ci link->conf->sync_device_ts = bss->device_ts_beacon; 735862306a36Sopenharmony_ci link->conf->sync_dtim_count = dtim_count; 735962306a36Sopenharmony_ci } 736062306a36Sopenharmony_ci 736162306a36Sopenharmony_ci elem = cfg80211_find_ext_elem(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION, 736262306a36Sopenharmony_ci beacon_ies->data, beacon_ies->len); 736362306a36Sopenharmony_ci if (elem && elem->datalen >= 3) 736462306a36Sopenharmony_ci link->conf->profile_periodicity = elem->data[2]; 736562306a36Sopenharmony_ci else 736662306a36Sopenharmony_ci link->conf->profile_periodicity = 0; 736762306a36Sopenharmony_ci 736862306a36Sopenharmony_ci elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, 736962306a36Sopenharmony_ci beacon_ies->data, beacon_ies->len); 737062306a36Sopenharmony_ci if (elem && elem->datalen >= 11 && 737162306a36Sopenharmony_ci (elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT)) 737262306a36Sopenharmony_ci link->conf->ema_ap = true; 737362306a36Sopenharmony_ci else 737462306a36Sopenharmony_ci link->conf->ema_ap = false; 737562306a36Sopenharmony_ci 737662306a36Sopenharmony_ci elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, 737762306a36Sopenharmony_ci beacon_ies->data, beacon_ies->len); 737862306a36Sopenharmony_ci eht_oper = (const void *)(elem->data + 1); 737962306a36Sopenharmony_ci 738062306a36Sopenharmony_ci if (elem && 738162306a36Sopenharmony_ci ieee80211_eht_oper_size_ok((const void *)(elem->data + 1), 738262306a36Sopenharmony_ci elem->datalen - 1) && 738362306a36Sopenharmony_ci (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) && 738462306a36Sopenharmony_ci (eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) { 738562306a36Sopenharmony_ci const struct ieee80211_eht_operation_info *info = 738662306a36Sopenharmony_ci (void *)eht_oper->optional; 738762306a36Sopenharmony_ci const u8 *disable_subchannel_bitmap = info->optional; 738862306a36Sopenharmony_ci u16 bitmap; 738962306a36Sopenharmony_ci 739062306a36Sopenharmony_ci bitmap = get_unaligned_le16(disable_subchannel_bitmap); 739162306a36Sopenharmony_ci if (cfg80211_valid_disable_subchannel_bitmap(&bitmap, 739262306a36Sopenharmony_ci &link->conf->chandef)) 739362306a36Sopenharmony_ci ieee80211_handle_puncturing_bitmap(link, 739462306a36Sopenharmony_ci eht_oper, 739562306a36Sopenharmony_ci bitmap, 739662306a36Sopenharmony_ci NULL); 739762306a36Sopenharmony_ci else 739862306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_EHT; 739962306a36Sopenharmony_ci } 740062306a36Sopenharmony_ci } 740162306a36Sopenharmony_ci rcu_read_unlock(); 740262306a36Sopenharmony_ci 740362306a36Sopenharmony_ci if (bss->corrupt_data) { 740462306a36Sopenharmony_ci char *corrupt_type = "data"; 740562306a36Sopenharmony_ci 740662306a36Sopenharmony_ci if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_BEACON) { 740762306a36Sopenharmony_ci if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) 740862306a36Sopenharmony_ci corrupt_type = "beacon and probe response"; 740962306a36Sopenharmony_ci else 741062306a36Sopenharmony_ci corrupt_type = "beacon"; 741162306a36Sopenharmony_ci } else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP) { 741262306a36Sopenharmony_ci corrupt_type = "probe response"; 741362306a36Sopenharmony_ci } 741462306a36Sopenharmony_ci sdata_info(sdata, "associating to AP %pM with corrupt %s\n", 741562306a36Sopenharmony_ci cbss->bssid, corrupt_type); 741662306a36Sopenharmony_ci } 741762306a36Sopenharmony_ci 741862306a36Sopenharmony_ci if (link->u.mgd.req_smps == IEEE80211_SMPS_AUTOMATIC) { 741962306a36Sopenharmony_ci if (sdata->u.mgd.powersave) 742062306a36Sopenharmony_ci link->smps_mode = IEEE80211_SMPS_DYNAMIC; 742162306a36Sopenharmony_ci else 742262306a36Sopenharmony_ci link->smps_mode = IEEE80211_SMPS_OFF; 742362306a36Sopenharmony_ci } else { 742462306a36Sopenharmony_ci link->smps_mode = link->u.mgd.req_smps; 742562306a36Sopenharmony_ci } 742662306a36Sopenharmony_ci 742762306a36Sopenharmony_ci return conn_flags; 742862306a36Sopenharmony_ci} 742962306a36Sopenharmony_ci 743062306a36Sopenharmony_ciint ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, 743162306a36Sopenharmony_ci struct cfg80211_assoc_request *req) 743262306a36Sopenharmony_ci{ 743362306a36Sopenharmony_ci unsigned int assoc_link_id = req->link_id < 0 ? 0 : req->link_id; 743462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 743562306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 743662306a36Sopenharmony_ci struct ieee80211_mgd_assoc_data *assoc_data; 743762306a36Sopenharmony_ci const struct element *ssid_elem; 743862306a36Sopenharmony_ci struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg; 743962306a36Sopenharmony_ci ieee80211_conn_flags_t conn_flags = 0; 744062306a36Sopenharmony_ci struct ieee80211_link_data *link; 744162306a36Sopenharmony_ci struct cfg80211_bss *cbss; 744262306a36Sopenharmony_ci struct ieee80211_bss *bss; 744362306a36Sopenharmony_ci bool override; 744462306a36Sopenharmony_ci int i, err; 744562306a36Sopenharmony_ci size_t size = sizeof(*assoc_data) + req->ie_len; 744662306a36Sopenharmony_ci 744762306a36Sopenharmony_ci for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) 744862306a36Sopenharmony_ci size += req->links[i].elems_len; 744962306a36Sopenharmony_ci 745062306a36Sopenharmony_ci /* FIXME: no support for 4-addr MLO yet */ 745162306a36Sopenharmony_ci if (sdata->u.mgd.use_4addr && req->link_id >= 0) 745262306a36Sopenharmony_ci return -EOPNOTSUPP; 745362306a36Sopenharmony_ci 745462306a36Sopenharmony_ci assoc_data = kzalloc(size, GFP_KERNEL); 745562306a36Sopenharmony_ci if (!assoc_data) 745662306a36Sopenharmony_ci return -ENOMEM; 745762306a36Sopenharmony_ci 745862306a36Sopenharmony_ci cbss = req->link_id < 0 ? req->bss : req->links[req->link_id].bss; 745962306a36Sopenharmony_ci 746062306a36Sopenharmony_ci rcu_read_lock(); 746162306a36Sopenharmony_ci ssid_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_SSID); 746262306a36Sopenharmony_ci if (!ssid_elem || ssid_elem->datalen > sizeof(assoc_data->ssid)) { 746362306a36Sopenharmony_ci rcu_read_unlock(); 746462306a36Sopenharmony_ci kfree(assoc_data); 746562306a36Sopenharmony_ci return -EINVAL; 746662306a36Sopenharmony_ci } 746762306a36Sopenharmony_ci memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen); 746862306a36Sopenharmony_ci assoc_data->ssid_len = ssid_elem->datalen; 746962306a36Sopenharmony_ci memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len); 747062306a36Sopenharmony_ci vif_cfg->ssid_len = assoc_data->ssid_len; 747162306a36Sopenharmony_ci rcu_read_unlock(); 747262306a36Sopenharmony_ci 747362306a36Sopenharmony_ci if (req->ap_mld_addr) { 747462306a36Sopenharmony_ci for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { 747562306a36Sopenharmony_ci if (!req->links[i].bss) 747662306a36Sopenharmony_ci continue; 747762306a36Sopenharmony_ci link = sdata_dereference(sdata->link[i], sdata); 747862306a36Sopenharmony_ci if (link) 747962306a36Sopenharmony_ci ether_addr_copy(assoc_data->link[i].addr, 748062306a36Sopenharmony_ci link->conf->addr); 748162306a36Sopenharmony_ci else 748262306a36Sopenharmony_ci eth_random_addr(assoc_data->link[i].addr); 748362306a36Sopenharmony_ci } 748462306a36Sopenharmony_ci } else { 748562306a36Sopenharmony_ci memcpy(assoc_data->link[0].addr, sdata->vif.addr, ETH_ALEN); 748662306a36Sopenharmony_ci } 748762306a36Sopenharmony_ci 748862306a36Sopenharmony_ci assoc_data->s1g = cbss->channel->band == NL80211_BAND_S1GHZ; 748962306a36Sopenharmony_ci 749062306a36Sopenharmony_ci memcpy(assoc_data->ap_addr, 749162306a36Sopenharmony_ci req->ap_mld_addr ?: req->bss->bssid, 749262306a36Sopenharmony_ci ETH_ALEN); 749362306a36Sopenharmony_ci 749462306a36Sopenharmony_ci if (ifmgd->associated) { 749562306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 749662306a36Sopenharmony_ci 749762306a36Sopenharmony_ci sdata_info(sdata, 749862306a36Sopenharmony_ci "disconnect from AP %pM for new assoc to %pM\n", 749962306a36Sopenharmony_ci sdata->vif.cfg.ap_addr, assoc_data->ap_addr); 750062306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, 750162306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, 750262306a36Sopenharmony_ci false, frame_buf); 750362306a36Sopenharmony_ci 750462306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, 750562306a36Sopenharmony_ci sizeof(frame_buf), true, 750662306a36Sopenharmony_ci WLAN_REASON_UNSPECIFIED, 750762306a36Sopenharmony_ci false); 750862306a36Sopenharmony_ci } 750962306a36Sopenharmony_ci 751062306a36Sopenharmony_ci if (ifmgd->auth_data && !ifmgd->auth_data->done) { 751162306a36Sopenharmony_ci err = -EBUSY; 751262306a36Sopenharmony_ci goto err_free; 751362306a36Sopenharmony_ci } 751462306a36Sopenharmony_ci 751562306a36Sopenharmony_ci if (ifmgd->assoc_data) { 751662306a36Sopenharmony_ci err = -EBUSY; 751762306a36Sopenharmony_ci goto err_free; 751862306a36Sopenharmony_ci } 751962306a36Sopenharmony_ci 752062306a36Sopenharmony_ci if (ifmgd->auth_data) { 752162306a36Sopenharmony_ci bool match; 752262306a36Sopenharmony_ci 752362306a36Sopenharmony_ci /* keep sta info, bssid if matching */ 752462306a36Sopenharmony_ci match = ether_addr_equal(ifmgd->auth_data->ap_addr, 752562306a36Sopenharmony_ci assoc_data->ap_addr) && 752662306a36Sopenharmony_ci ifmgd->auth_data->link_id == req->link_id; 752762306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, match); 752862306a36Sopenharmony_ci } 752962306a36Sopenharmony_ci 753062306a36Sopenharmony_ci /* prepare assoc data */ 753162306a36Sopenharmony_ci 753262306a36Sopenharmony_ci bss = (void *)cbss->priv; 753362306a36Sopenharmony_ci assoc_data->wmm = bss->wmm_used && 753462306a36Sopenharmony_ci (local->hw.queues >= IEEE80211_NUM_ACS); 753562306a36Sopenharmony_ci 753662306a36Sopenharmony_ci /* 753762306a36Sopenharmony_ci * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. 753862306a36Sopenharmony_ci * We still associate in non-HT mode (11a/b/g) if any one of these 753962306a36Sopenharmony_ci * ciphers is configured as pairwise. 754062306a36Sopenharmony_ci * We can set this to true for non-11n hardware, that'll be checked 754162306a36Sopenharmony_ci * separately along with the peer capabilities. 754262306a36Sopenharmony_ci */ 754362306a36Sopenharmony_ci for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) { 754462306a36Sopenharmony_ci if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || 754562306a36Sopenharmony_ci req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || 754662306a36Sopenharmony_ci req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { 754762306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HT; 754862306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_VHT; 754962306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HE; 755062306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_EHT; 755162306a36Sopenharmony_ci netdev_info(sdata->dev, 755262306a36Sopenharmony_ci "disabling HT/VHT/HE due to WEP/TKIP use\n"); 755362306a36Sopenharmony_ci } 755462306a36Sopenharmony_ci } 755562306a36Sopenharmony_ci 755662306a36Sopenharmony_ci /* also disable HT/VHT/HE/EHT if the AP doesn't use WMM */ 755762306a36Sopenharmony_ci if (!bss->wmm_used) { 755862306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HT; 755962306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_VHT; 756062306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HE; 756162306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_EHT; 756262306a36Sopenharmony_ci netdev_info(sdata->dev, 756362306a36Sopenharmony_ci "disabling HT/VHT/HE as WMM/QoS is not supported by the AP\n"); 756462306a36Sopenharmony_ci } 756562306a36Sopenharmony_ci 756662306a36Sopenharmony_ci if (req->flags & ASSOC_REQ_DISABLE_HT) { 756762306a36Sopenharmony_ci mlme_dbg(sdata, "HT disabled by flag, disabling HT/VHT/HE\n"); 756862306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HT; 756962306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_VHT; 757062306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HE; 757162306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_EHT; 757262306a36Sopenharmony_ci } 757362306a36Sopenharmony_ci 757462306a36Sopenharmony_ci if (req->flags & ASSOC_REQ_DISABLE_VHT) { 757562306a36Sopenharmony_ci mlme_dbg(sdata, "VHT disabled by flag, disabling VHT\n"); 757662306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_VHT; 757762306a36Sopenharmony_ci } 757862306a36Sopenharmony_ci 757962306a36Sopenharmony_ci if (req->flags & ASSOC_REQ_DISABLE_HE) { 758062306a36Sopenharmony_ci mlme_dbg(sdata, "HE disabled by flag, disabling HE/EHT\n"); 758162306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_HE; 758262306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_EHT; 758362306a36Sopenharmony_ci } 758462306a36Sopenharmony_ci 758562306a36Sopenharmony_ci if (req->flags & ASSOC_REQ_DISABLE_EHT) 758662306a36Sopenharmony_ci conn_flags |= IEEE80211_CONN_DISABLE_EHT; 758762306a36Sopenharmony_ci 758862306a36Sopenharmony_ci memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa)); 758962306a36Sopenharmony_ci memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask, 759062306a36Sopenharmony_ci sizeof(ifmgd->ht_capa_mask)); 759162306a36Sopenharmony_ci 759262306a36Sopenharmony_ci memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa)); 759362306a36Sopenharmony_ci memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask, 759462306a36Sopenharmony_ci sizeof(ifmgd->vht_capa_mask)); 759562306a36Sopenharmony_ci 759662306a36Sopenharmony_ci memcpy(&ifmgd->s1g_capa, &req->s1g_capa, sizeof(ifmgd->s1g_capa)); 759762306a36Sopenharmony_ci memcpy(&ifmgd->s1g_capa_mask, &req->s1g_capa_mask, 759862306a36Sopenharmony_ci sizeof(ifmgd->s1g_capa_mask)); 759962306a36Sopenharmony_ci 760062306a36Sopenharmony_ci if (req->ie && req->ie_len) { 760162306a36Sopenharmony_ci memcpy(assoc_data->ie, req->ie, req->ie_len); 760262306a36Sopenharmony_ci assoc_data->ie_len = req->ie_len; 760362306a36Sopenharmony_ci assoc_data->ie_pos = assoc_data->ie + assoc_data->ie_len; 760462306a36Sopenharmony_ci } else { 760562306a36Sopenharmony_ci assoc_data->ie_pos = assoc_data->ie; 760662306a36Sopenharmony_ci } 760762306a36Sopenharmony_ci 760862306a36Sopenharmony_ci if (req->fils_kek) { 760962306a36Sopenharmony_ci /* should already be checked in cfg80211 - so warn */ 761062306a36Sopenharmony_ci if (WARN_ON(req->fils_kek_len > FILS_MAX_KEK_LEN)) { 761162306a36Sopenharmony_ci err = -EINVAL; 761262306a36Sopenharmony_ci goto err_free; 761362306a36Sopenharmony_ci } 761462306a36Sopenharmony_ci memcpy(assoc_data->fils_kek, req->fils_kek, 761562306a36Sopenharmony_ci req->fils_kek_len); 761662306a36Sopenharmony_ci assoc_data->fils_kek_len = req->fils_kek_len; 761762306a36Sopenharmony_ci } 761862306a36Sopenharmony_ci 761962306a36Sopenharmony_ci if (req->fils_nonces) 762062306a36Sopenharmony_ci memcpy(assoc_data->fils_nonces, req->fils_nonces, 762162306a36Sopenharmony_ci 2 * FILS_NONCE_LEN); 762262306a36Sopenharmony_ci 762362306a36Sopenharmony_ci /* default timeout */ 762462306a36Sopenharmony_ci assoc_data->timeout = jiffies; 762562306a36Sopenharmony_ci assoc_data->timeout_started = true; 762662306a36Sopenharmony_ci 762762306a36Sopenharmony_ci assoc_data->assoc_link_id = assoc_link_id; 762862306a36Sopenharmony_ci 762962306a36Sopenharmony_ci if (req->ap_mld_addr) { 763062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) { 763162306a36Sopenharmony_ci assoc_data->link[i].conn_flags = conn_flags; 763262306a36Sopenharmony_ci assoc_data->link[i].bss = req->links[i].bss; 763362306a36Sopenharmony_ci assoc_data->link[i].disabled = req->links[i].disabled; 763462306a36Sopenharmony_ci } 763562306a36Sopenharmony_ci 763662306a36Sopenharmony_ci /* if there was no authentication, set up the link */ 763762306a36Sopenharmony_ci err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id), 0); 763862306a36Sopenharmony_ci if (err) 763962306a36Sopenharmony_ci goto err_clear; 764062306a36Sopenharmony_ci } else { 764162306a36Sopenharmony_ci assoc_data->link[0].conn_flags = conn_flags; 764262306a36Sopenharmony_ci assoc_data->link[0].bss = cbss; 764362306a36Sopenharmony_ci } 764462306a36Sopenharmony_ci 764562306a36Sopenharmony_ci link = sdata_dereference(sdata->link[assoc_link_id], sdata); 764662306a36Sopenharmony_ci if (WARN_ON(!link)) { 764762306a36Sopenharmony_ci err = -EINVAL; 764862306a36Sopenharmony_ci goto err_clear; 764962306a36Sopenharmony_ci } 765062306a36Sopenharmony_ci 765162306a36Sopenharmony_ci /* keep old conn_flags from ieee80211_prep_channel() from auth */ 765262306a36Sopenharmony_ci conn_flags |= link->u.mgd.conn_flags; 765362306a36Sopenharmony_ci conn_flags |= ieee80211_setup_assoc_link(sdata, assoc_data, req, 765462306a36Sopenharmony_ci conn_flags, assoc_link_id); 765562306a36Sopenharmony_ci override = link->u.mgd.conn_flags != conn_flags; 765662306a36Sopenharmony_ci link->u.mgd.conn_flags |= conn_flags; 765762306a36Sopenharmony_ci 765862306a36Sopenharmony_ci if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) && 765962306a36Sopenharmony_ci ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK), 766062306a36Sopenharmony_ci "U-APSD not supported with HW_PS_NULLFUNC_STACK\n")) 766162306a36Sopenharmony_ci sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; 766262306a36Sopenharmony_ci 766362306a36Sopenharmony_ci if (bss->wmm_used && bss->uapsd_supported && 766462306a36Sopenharmony_ci (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) { 766562306a36Sopenharmony_ci assoc_data->uapsd = true; 766662306a36Sopenharmony_ci ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; 766762306a36Sopenharmony_ci } else { 766862306a36Sopenharmony_ci assoc_data->uapsd = false; 766962306a36Sopenharmony_ci ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; 767062306a36Sopenharmony_ci } 767162306a36Sopenharmony_ci 767262306a36Sopenharmony_ci if (req->prev_bssid) 767362306a36Sopenharmony_ci memcpy(assoc_data->prev_ap_addr, req->prev_bssid, ETH_ALEN); 767462306a36Sopenharmony_ci 767562306a36Sopenharmony_ci if (req->use_mfp) { 767662306a36Sopenharmony_ci ifmgd->mfp = IEEE80211_MFP_REQUIRED; 767762306a36Sopenharmony_ci ifmgd->flags |= IEEE80211_STA_MFP_ENABLED; 767862306a36Sopenharmony_ci } else { 767962306a36Sopenharmony_ci ifmgd->mfp = IEEE80211_MFP_DISABLED; 768062306a36Sopenharmony_ci ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED; 768162306a36Sopenharmony_ci } 768262306a36Sopenharmony_ci 768362306a36Sopenharmony_ci if (req->flags & ASSOC_REQ_USE_RRM) 768462306a36Sopenharmony_ci ifmgd->flags |= IEEE80211_STA_ENABLE_RRM; 768562306a36Sopenharmony_ci else 768662306a36Sopenharmony_ci ifmgd->flags &= ~IEEE80211_STA_ENABLE_RRM; 768762306a36Sopenharmony_ci 768862306a36Sopenharmony_ci if (req->crypto.control_port) 768962306a36Sopenharmony_ci ifmgd->flags |= IEEE80211_STA_CONTROL_PORT; 769062306a36Sopenharmony_ci else 769162306a36Sopenharmony_ci ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT; 769262306a36Sopenharmony_ci 769362306a36Sopenharmony_ci sdata->control_port_protocol = req->crypto.control_port_ethertype; 769462306a36Sopenharmony_ci sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt; 769562306a36Sopenharmony_ci sdata->control_port_over_nl80211 = 769662306a36Sopenharmony_ci req->crypto.control_port_over_nl80211; 769762306a36Sopenharmony_ci sdata->control_port_no_preauth = req->crypto.control_port_no_preauth; 769862306a36Sopenharmony_ci 769962306a36Sopenharmony_ci /* kick off associate process */ 770062306a36Sopenharmony_ci ifmgd->assoc_data = assoc_data; 770162306a36Sopenharmony_ci 770262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) { 770362306a36Sopenharmony_ci if (!assoc_data->link[i].bss) 770462306a36Sopenharmony_ci continue; 770562306a36Sopenharmony_ci if (i == assoc_data->assoc_link_id) 770662306a36Sopenharmony_ci continue; 770762306a36Sopenharmony_ci /* only calculate the flags, hence link == NULL */ 770862306a36Sopenharmony_ci err = ieee80211_prep_channel(sdata, NULL, assoc_data->link[i].bss, 770962306a36Sopenharmony_ci &assoc_data->link[i].conn_flags); 771062306a36Sopenharmony_ci if (err) 771162306a36Sopenharmony_ci goto err_clear; 771262306a36Sopenharmony_ci } 771362306a36Sopenharmony_ci 771462306a36Sopenharmony_ci /* needed for transmitting the assoc frames properly */ 771562306a36Sopenharmony_ci memcpy(sdata->vif.cfg.ap_addr, assoc_data->ap_addr, ETH_ALEN); 771662306a36Sopenharmony_ci 771762306a36Sopenharmony_ci err = ieee80211_prep_connection(sdata, cbss, req->link_id, 771862306a36Sopenharmony_ci req->ap_mld_addr, true, override); 771962306a36Sopenharmony_ci if (err) 772062306a36Sopenharmony_ci goto err_clear; 772162306a36Sopenharmony_ci 772262306a36Sopenharmony_ci assoc_data->link[assoc_data->assoc_link_id].conn_flags = 772362306a36Sopenharmony_ci link->u.mgd.conn_flags; 772462306a36Sopenharmony_ci 772562306a36Sopenharmony_ci if (ieee80211_hw_check(&sdata->local->hw, NEED_DTIM_BEFORE_ASSOC)) { 772662306a36Sopenharmony_ci const struct cfg80211_bss_ies *beacon_ies; 772762306a36Sopenharmony_ci 772862306a36Sopenharmony_ci rcu_read_lock(); 772962306a36Sopenharmony_ci beacon_ies = rcu_dereference(req->bss->beacon_ies); 773062306a36Sopenharmony_ci if (!beacon_ies) { 773162306a36Sopenharmony_ci /* 773262306a36Sopenharmony_ci * Wait up to one beacon interval ... 773362306a36Sopenharmony_ci * should this be more if we miss one? 773462306a36Sopenharmony_ci */ 773562306a36Sopenharmony_ci sdata_info(sdata, "waiting for beacon from %pM\n", 773662306a36Sopenharmony_ci link->u.mgd.bssid); 773762306a36Sopenharmony_ci assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval); 773862306a36Sopenharmony_ci assoc_data->timeout_started = true; 773962306a36Sopenharmony_ci assoc_data->need_beacon = true; 774062306a36Sopenharmony_ci } 774162306a36Sopenharmony_ci rcu_read_unlock(); 774262306a36Sopenharmony_ci } 774362306a36Sopenharmony_ci 774462306a36Sopenharmony_ci run_again(sdata, assoc_data->timeout); 774562306a36Sopenharmony_ci 774662306a36Sopenharmony_ci return 0; 774762306a36Sopenharmony_ci err_clear: 774862306a36Sopenharmony_ci eth_zero_addr(sdata->deflink.u.mgd.bssid); 774962306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, &sdata->deflink, 775062306a36Sopenharmony_ci BSS_CHANGED_BSSID); 775162306a36Sopenharmony_ci ifmgd->assoc_data = NULL; 775262306a36Sopenharmony_ci err_free: 775362306a36Sopenharmony_ci kfree(assoc_data); 775462306a36Sopenharmony_ci return err; 775562306a36Sopenharmony_ci} 775662306a36Sopenharmony_ci 775762306a36Sopenharmony_ciint ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, 775862306a36Sopenharmony_ci struct cfg80211_deauth_request *req) 775962306a36Sopenharmony_ci{ 776062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 776162306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 776262306a36Sopenharmony_ci bool tx = !req->local_state_change; 776362306a36Sopenharmony_ci struct ieee80211_prep_tx_info info = { 776462306a36Sopenharmony_ci .subtype = IEEE80211_STYPE_DEAUTH, 776562306a36Sopenharmony_ci }; 776662306a36Sopenharmony_ci 776762306a36Sopenharmony_ci if (ifmgd->auth_data && 776862306a36Sopenharmony_ci ether_addr_equal(ifmgd->auth_data->ap_addr, req->bssid)) { 776962306a36Sopenharmony_ci sdata_info(sdata, 777062306a36Sopenharmony_ci "aborting authentication with %pM by local choice (Reason: %u=%s)\n", 777162306a36Sopenharmony_ci req->bssid, req->reason_code, 777262306a36Sopenharmony_ci ieee80211_get_reason_code_string(req->reason_code)); 777362306a36Sopenharmony_ci 777462306a36Sopenharmony_ci drv_mgd_prepare_tx(sdata->local, sdata, &info); 777562306a36Sopenharmony_ci ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid, 777662306a36Sopenharmony_ci IEEE80211_STYPE_DEAUTH, 777762306a36Sopenharmony_ci req->reason_code, tx, 777862306a36Sopenharmony_ci frame_buf); 777962306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, false); 778062306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, 778162306a36Sopenharmony_ci sizeof(frame_buf), true, 778262306a36Sopenharmony_ci req->reason_code, false); 778362306a36Sopenharmony_ci drv_mgd_complete_tx(sdata->local, sdata, &info); 778462306a36Sopenharmony_ci return 0; 778562306a36Sopenharmony_ci } 778662306a36Sopenharmony_ci 778762306a36Sopenharmony_ci if (ifmgd->assoc_data && 778862306a36Sopenharmony_ci ether_addr_equal(ifmgd->assoc_data->ap_addr, req->bssid)) { 778962306a36Sopenharmony_ci sdata_info(sdata, 779062306a36Sopenharmony_ci "aborting association with %pM by local choice (Reason: %u=%s)\n", 779162306a36Sopenharmony_ci req->bssid, req->reason_code, 779262306a36Sopenharmony_ci ieee80211_get_reason_code_string(req->reason_code)); 779362306a36Sopenharmony_ci 779462306a36Sopenharmony_ci drv_mgd_prepare_tx(sdata->local, sdata, &info); 779562306a36Sopenharmony_ci ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid, 779662306a36Sopenharmony_ci IEEE80211_STYPE_DEAUTH, 779762306a36Sopenharmony_ci req->reason_code, tx, 779862306a36Sopenharmony_ci frame_buf); 779962306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON); 780062306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, 780162306a36Sopenharmony_ci sizeof(frame_buf), true, 780262306a36Sopenharmony_ci req->reason_code, false); 780362306a36Sopenharmony_ci drv_mgd_complete_tx(sdata->local, sdata, &info); 780462306a36Sopenharmony_ci return 0; 780562306a36Sopenharmony_ci } 780662306a36Sopenharmony_ci 780762306a36Sopenharmony_ci if (ifmgd->associated && 780862306a36Sopenharmony_ci ether_addr_equal(sdata->vif.cfg.ap_addr, req->bssid)) { 780962306a36Sopenharmony_ci sdata_info(sdata, 781062306a36Sopenharmony_ci "deauthenticating from %pM by local choice (Reason: %u=%s)\n", 781162306a36Sopenharmony_ci req->bssid, req->reason_code, 781262306a36Sopenharmony_ci ieee80211_get_reason_code_string(req->reason_code)); 781362306a36Sopenharmony_ci 781462306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, 781562306a36Sopenharmony_ci req->reason_code, tx, frame_buf); 781662306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, 781762306a36Sopenharmony_ci sizeof(frame_buf), true, 781862306a36Sopenharmony_ci req->reason_code, false); 781962306a36Sopenharmony_ci drv_mgd_complete_tx(sdata->local, sdata, &info); 782062306a36Sopenharmony_ci return 0; 782162306a36Sopenharmony_ci } 782262306a36Sopenharmony_ci 782362306a36Sopenharmony_ci return -ENOTCONN; 782462306a36Sopenharmony_ci} 782562306a36Sopenharmony_ci 782662306a36Sopenharmony_ciint ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, 782762306a36Sopenharmony_ci struct cfg80211_disassoc_request *req) 782862306a36Sopenharmony_ci{ 782962306a36Sopenharmony_ci u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; 783062306a36Sopenharmony_ci 783162306a36Sopenharmony_ci if (!sdata->u.mgd.associated || 783262306a36Sopenharmony_ci memcmp(sdata->vif.cfg.ap_addr, req->ap_addr, ETH_ALEN)) 783362306a36Sopenharmony_ci return -ENOTCONN; 783462306a36Sopenharmony_ci 783562306a36Sopenharmony_ci sdata_info(sdata, 783662306a36Sopenharmony_ci "disassociating from %pM by local choice (Reason: %u=%s)\n", 783762306a36Sopenharmony_ci req->ap_addr, req->reason_code, 783862306a36Sopenharmony_ci ieee80211_get_reason_code_string(req->reason_code)); 783962306a36Sopenharmony_ci 784062306a36Sopenharmony_ci ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC, 784162306a36Sopenharmony_ci req->reason_code, !req->local_state_change, 784262306a36Sopenharmony_ci frame_buf); 784362306a36Sopenharmony_ci 784462306a36Sopenharmony_ci ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, 784562306a36Sopenharmony_ci req->reason_code, false); 784662306a36Sopenharmony_ci 784762306a36Sopenharmony_ci return 0; 784862306a36Sopenharmony_ci} 784962306a36Sopenharmony_ci 785062306a36Sopenharmony_civoid ieee80211_mgd_stop_link(struct ieee80211_link_data *link) 785162306a36Sopenharmony_ci{ 785262306a36Sopenharmony_ci wiphy_work_cancel(link->sdata->local->hw.wiphy, 785362306a36Sopenharmony_ci &link->u.mgd.request_smps_work); 785462306a36Sopenharmony_ci wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy, 785562306a36Sopenharmony_ci &link->u.mgd.chswitch_work); 785662306a36Sopenharmony_ci} 785762306a36Sopenharmony_ci 785862306a36Sopenharmony_civoid ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) 785962306a36Sopenharmony_ci{ 786062306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 786162306a36Sopenharmony_ci 786262306a36Sopenharmony_ci /* 786362306a36Sopenharmony_ci * Make sure some work items will not run after this, 786462306a36Sopenharmony_ci * they will not do anything but might not have been 786562306a36Sopenharmony_ci * cancelled when disconnecting. 786662306a36Sopenharmony_ci */ 786762306a36Sopenharmony_ci cancel_work_sync(&ifmgd->monitor_work); 786862306a36Sopenharmony_ci wiphy_work_cancel(sdata->local->hw.wiphy, 786962306a36Sopenharmony_ci &ifmgd->beacon_connection_loss_work); 787062306a36Sopenharmony_ci wiphy_work_cancel(sdata->local->hw.wiphy, 787162306a36Sopenharmony_ci &ifmgd->csa_connection_drop_work); 787262306a36Sopenharmony_ci cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work); 787362306a36Sopenharmony_ci wiphy_delayed_work_cancel(sdata->local->hw.wiphy, 787462306a36Sopenharmony_ci &ifmgd->ml_reconf_work); 787562306a36Sopenharmony_ci 787662306a36Sopenharmony_ci sdata_lock(sdata); 787762306a36Sopenharmony_ci if (ifmgd->assoc_data) 787862306a36Sopenharmony_ci ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT); 787962306a36Sopenharmony_ci if (ifmgd->auth_data) 788062306a36Sopenharmony_ci ieee80211_destroy_auth_data(sdata, false); 788162306a36Sopenharmony_ci spin_lock_bh(&ifmgd->teardown_lock); 788262306a36Sopenharmony_ci if (ifmgd->teardown_skb) { 788362306a36Sopenharmony_ci kfree_skb(ifmgd->teardown_skb); 788462306a36Sopenharmony_ci ifmgd->teardown_skb = NULL; 788562306a36Sopenharmony_ci ifmgd->orig_teardown_skb = NULL; 788662306a36Sopenharmony_ci } 788762306a36Sopenharmony_ci kfree(ifmgd->assoc_req_ies); 788862306a36Sopenharmony_ci ifmgd->assoc_req_ies = NULL; 788962306a36Sopenharmony_ci ifmgd->assoc_req_ies_len = 0; 789062306a36Sopenharmony_ci spin_unlock_bh(&ifmgd->teardown_lock); 789162306a36Sopenharmony_ci del_timer_sync(&ifmgd->timer); 789262306a36Sopenharmony_ci sdata_unlock(sdata); 789362306a36Sopenharmony_ci} 789462306a36Sopenharmony_ci 789562306a36Sopenharmony_civoid ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, 789662306a36Sopenharmony_ci enum nl80211_cqm_rssi_threshold_event rssi_event, 789762306a36Sopenharmony_ci s32 rssi_level, 789862306a36Sopenharmony_ci gfp_t gfp) 789962306a36Sopenharmony_ci{ 790062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 790162306a36Sopenharmony_ci 790262306a36Sopenharmony_ci trace_api_cqm_rssi_notify(sdata, rssi_event, rssi_level); 790362306a36Sopenharmony_ci 790462306a36Sopenharmony_ci cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, rssi_level, gfp); 790562306a36Sopenharmony_ci} 790662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_cqm_rssi_notify); 790762306a36Sopenharmony_ci 790862306a36Sopenharmony_civoid ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp) 790962306a36Sopenharmony_ci{ 791062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 791162306a36Sopenharmony_ci 791262306a36Sopenharmony_ci trace_api_cqm_beacon_loss_notify(sdata->local, sdata); 791362306a36Sopenharmony_ci 791462306a36Sopenharmony_ci cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp); 791562306a36Sopenharmony_ci} 791662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify); 791762306a36Sopenharmony_ci 791862306a36Sopenharmony_cistatic void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, 791962306a36Sopenharmony_ci int rssi_min_thold, 792062306a36Sopenharmony_ci int rssi_max_thold) 792162306a36Sopenharmony_ci{ 792262306a36Sopenharmony_ci trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); 792362306a36Sopenharmony_ci 792462306a36Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 792562306a36Sopenharmony_ci return; 792662306a36Sopenharmony_ci 792762306a36Sopenharmony_ci /* 792862306a36Sopenharmony_ci * Scale up threshold values before storing it, as the RSSI averaging 792962306a36Sopenharmony_ci * algorithm uses a scaled up value as well. Change this scaling 793062306a36Sopenharmony_ci * factor if the RSSI averaging algorithm changes. 793162306a36Sopenharmony_ci */ 793262306a36Sopenharmony_ci sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; 793362306a36Sopenharmony_ci sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; 793462306a36Sopenharmony_ci} 793562306a36Sopenharmony_ci 793662306a36Sopenharmony_civoid ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, 793762306a36Sopenharmony_ci int rssi_min_thold, 793862306a36Sopenharmony_ci int rssi_max_thold) 793962306a36Sopenharmony_ci{ 794062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 794162306a36Sopenharmony_ci 794262306a36Sopenharmony_ci WARN_ON(rssi_min_thold == rssi_max_thold || 794362306a36Sopenharmony_ci rssi_min_thold > rssi_max_thold); 794462306a36Sopenharmony_ci 794562306a36Sopenharmony_ci _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, 794662306a36Sopenharmony_ci rssi_max_thold); 794762306a36Sopenharmony_ci} 794862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_enable_rssi_reports); 794962306a36Sopenharmony_ci 795062306a36Sopenharmony_civoid ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) 795162306a36Sopenharmony_ci{ 795262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 795362306a36Sopenharmony_ci 795462306a36Sopenharmony_ci _ieee80211_enable_rssi_reports(sdata, 0, 0); 795562306a36Sopenharmony_ci} 795662306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_disable_rssi_reports); 7957