162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2002-2005, Instant802 Networks, Inc. 462306a36Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc. 562306a36Sopenharmony_ci * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 662306a36Sopenharmony_ci * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 762306a36Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 862306a36Sopenharmony_ci * Copyright (C) 2015-2017 Intel Deutschland GmbH 962306a36Sopenharmony_ci * Copyright (C) 2018-2023 Intel Corporation 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * utilities for mac80211 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <net/mac80211.h> 1562306a36Sopenharmony_ci#include <linux/netdevice.h> 1662306a36Sopenharmony_ci#include <linux/export.h> 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/skbuff.h> 2062306a36Sopenharmony_ci#include <linux/etherdevice.h> 2162306a36Sopenharmony_ci#include <linux/if_arp.h> 2262306a36Sopenharmony_ci#include <linux/bitmap.h> 2362306a36Sopenharmony_ci#include <linux/crc32.h> 2462306a36Sopenharmony_ci#include <net/net_namespace.h> 2562306a36Sopenharmony_ci#include <net/cfg80211.h> 2662306a36Sopenharmony_ci#include <net/rtnetlink.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "ieee80211_i.h" 2962306a36Sopenharmony_ci#include "driver-ops.h" 3062306a36Sopenharmony_ci#include "rate.h" 3162306a36Sopenharmony_ci#include "mesh.h" 3262306a36Sopenharmony_ci#include "wme.h" 3362306a36Sopenharmony_ci#include "led.h" 3462306a36Sopenharmony_ci#include "wep.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* privid for wiphys to determine whether they belong to us or not */ 3762306a36Sopenharmony_ciconst void *const mac80211_wiphy_privid = &mac80211_wiphy_privid; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct ieee80211_local *local; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci local = wiphy_priv(wiphy); 4462306a36Sopenharmony_ci return &local->hw; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ciEXPORT_SYMBOL(wiphy_to_ieee80211_hw); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciu8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, 4962306a36Sopenharmony_ci enum nl80211_iftype type) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci __le16 fc = hdr->frame_control; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (ieee80211_is_data(fc)) { 5462306a36Sopenharmony_ci if (len < 24) /* drop incorrect hdr len (data) */ 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (ieee80211_has_a4(fc)) 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci if (ieee80211_has_tods(fc)) 6062306a36Sopenharmony_ci return hdr->addr1; 6162306a36Sopenharmony_ci if (ieee80211_has_fromds(fc)) 6262306a36Sopenharmony_ci return hdr->addr2; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return hdr->addr3; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (ieee80211_is_s1g_beacon(fc)) { 6862306a36Sopenharmony_ci struct ieee80211_ext *ext = (void *) hdr; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return ext->u.s1g_beacon.sa; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (ieee80211_is_mgmt(fc)) { 7462306a36Sopenharmony_ci if (len < 24) /* drop incorrect hdr len (mgmt) */ 7562306a36Sopenharmony_ci return NULL; 7662306a36Sopenharmony_ci return hdr->addr3; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (ieee80211_is_ctl(fc)) { 8062306a36Sopenharmony_ci if (ieee80211_is_pspoll(fc)) 8162306a36Sopenharmony_ci return hdr->addr1; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (ieee80211_is_back_req(fc)) { 8462306a36Sopenharmony_ci switch (type) { 8562306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 8662306a36Sopenharmony_ci return hdr->addr2; 8762306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 8862306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 8962306a36Sopenharmony_ci return hdr->addr1; 9062306a36Sopenharmony_ci default: 9162306a36Sopenharmony_ci break; /* fall through to the return */ 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return NULL; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_get_bssid); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_civoid ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct sk_buff *skb; 10362306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci skb_queue_walk(&tx->skbs, skb) { 10662306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *) skb->data; 10762306a36Sopenharmony_ci hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciint ieee80211_frame_duration(enum nl80211_band band, size_t len, 11262306a36Sopenharmony_ci int rate, int erp, int short_preamble, 11362306a36Sopenharmony_ci int shift) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int dur; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* calculate duration (in microseconds, rounded up to next higher 11862306a36Sopenharmony_ci * integer if it includes a fractional microsecond) to send frame of 11962306a36Sopenharmony_ci * len bytes (does not include FCS) at the given rate. Duration will 12062306a36Sopenharmony_ci * also include SIFS. 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * rate is in 100 kbps, so divident is multiplied by 10 in the 12362306a36Sopenharmony_ci * DIV_ROUND_UP() operations. 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and 12662306a36Sopenharmony_ci * is assumed to be 0 otherwise. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ || erp) { 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * OFDM: 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * N_DBPS = DATARATE x 4 13462306a36Sopenharmony_ci * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) 13562306a36Sopenharmony_ci * (16 = SIGNAL time, 6 = tail bits) 13662306a36Sopenharmony_ci * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * T_SYM = 4 usec 13962306a36Sopenharmony_ci * 802.11a - 18.5.2: aSIFSTime = 16 usec 14062306a36Sopenharmony_ci * 802.11g - 19.8.4: aSIFSTime = 10 usec + 14162306a36Sopenharmony_ci * signal ext = 6 usec 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci dur = 16; /* SIFS + signal ext */ 14462306a36Sopenharmony_ci dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ 14562306a36Sopenharmony_ci dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* IEEE 802.11-2012 18.3.2.4: all values above are: 14862306a36Sopenharmony_ci * * times 4 for 5 MHz 14962306a36Sopenharmony_ci * * times 2 for 10 MHz 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci dur *= 1 << shift; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* rates should already consider the channel bandwidth, 15462306a36Sopenharmony_ci * don't apply divisor again. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 15762306a36Sopenharmony_ci 4 * rate); /* T_SYM x N_SYM */ 15862306a36Sopenharmony_ci } else { 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * 802.11b or 802.11g with 802.11b compatibility: 16162306a36Sopenharmony_ci * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + 16262306a36Sopenharmony_ci * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 16562306a36Sopenharmony_ci * aSIFSTime = 10 usec 16662306a36Sopenharmony_ci * aPreambleLength = 144 usec or 72 usec with short preamble 16762306a36Sopenharmony_ci * aPLCPHeaderLength = 48 usec or 24 usec with short preamble 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci dur = 10; /* aSIFSTime = 10 usec */ 17062306a36Sopenharmony_ci dur += short_preamble ? (72 + 24) : (144 + 48); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return dur; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* Exported duration function for driver use */ 17962306a36Sopenharmony_ci__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, 18062306a36Sopenharmony_ci struct ieee80211_vif *vif, 18162306a36Sopenharmony_ci enum nl80211_band band, 18262306a36Sopenharmony_ci size_t frame_len, 18362306a36Sopenharmony_ci struct ieee80211_rate *rate) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 18662306a36Sopenharmony_ci u16 dur; 18762306a36Sopenharmony_ci int erp, shift = 0; 18862306a36Sopenharmony_ci bool short_preamble = false; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci erp = 0; 19162306a36Sopenharmony_ci if (vif) { 19262306a36Sopenharmony_ci sdata = vif_to_sdata(vif); 19362306a36Sopenharmony_ci short_preamble = sdata->vif.bss_conf.use_short_preamble; 19462306a36Sopenharmony_ci if (sdata->deflink.operating_11g_mode) 19562306a36Sopenharmony_ci erp = rate->flags & IEEE80211_RATE_ERP_G; 19662306a36Sopenharmony_ci shift = ieee80211_vif_get_shift(vif); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, 20062306a36Sopenharmony_ci short_preamble, shift); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return cpu_to_le16(dur); 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_generic_frame_duration); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, 20762306a36Sopenharmony_ci struct ieee80211_vif *vif, size_t frame_len, 20862306a36Sopenharmony_ci const struct ieee80211_tx_info *frame_txctl) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 21162306a36Sopenharmony_ci struct ieee80211_rate *rate; 21262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 21362306a36Sopenharmony_ci bool short_preamble; 21462306a36Sopenharmony_ci int erp, shift = 0, bitrate; 21562306a36Sopenharmony_ci u16 dur; 21662306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci sband = local->hw.wiphy->bands[frame_txctl->band]; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci short_preamble = false; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci erp = 0; 22562306a36Sopenharmony_ci if (vif) { 22662306a36Sopenharmony_ci sdata = vif_to_sdata(vif); 22762306a36Sopenharmony_ci short_preamble = sdata->vif.bss_conf.use_short_preamble; 22862306a36Sopenharmony_ci if (sdata->deflink.operating_11g_mode) 22962306a36Sopenharmony_ci erp = rate->flags & IEEE80211_RATE_ERP_G; 23062306a36Sopenharmony_ci shift = ieee80211_vif_get_shift(vif); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* CTS duration */ 23662306a36Sopenharmony_ci dur = ieee80211_frame_duration(sband->band, 10, bitrate, 23762306a36Sopenharmony_ci erp, short_preamble, shift); 23862306a36Sopenharmony_ci /* Data frame duration */ 23962306a36Sopenharmony_ci dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, 24062306a36Sopenharmony_ci erp, short_preamble, shift); 24162306a36Sopenharmony_ci /* ACK duration */ 24262306a36Sopenharmony_ci dur += ieee80211_frame_duration(sband->band, 10, bitrate, 24362306a36Sopenharmony_ci erp, short_preamble, shift); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return cpu_to_le16(dur); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_rts_duration); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, 25062306a36Sopenharmony_ci struct ieee80211_vif *vif, 25162306a36Sopenharmony_ci size_t frame_len, 25262306a36Sopenharmony_ci const struct ieee80211_tx_info *frame_txctl) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 25562306a36Sopenharmony_ci struct ieee80211_rate *rate; 25662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 25762306a36Sopenharmony_ci bool short_preamble; 25862306a36Sopenharmony_ci int erp, shift = 0, bitrate; 25962306a36Sopenharmony_ci u16 dur; 26062306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci sband = local->hw.wiphy->bands[frame_txctl->band]; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci short_preamble = false; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 26762306a36Sopenharmony_ci erp = 0; 26862306a36Sopenharmony_ci if (vif) { 26962306a36Sopenharmony_ci sdata = vif_to_sdata(vif); 27062306a36Sopenharmony_ci short_preamble = sdata->vif.bss_conf.use_short_preamble; 27162306a36Sopenharmony_ci if (sdata->deflink.operating_11g_mode) 27262306a36Sopenharmony_ci erp = rate->flags & IEEE80211_RATE_ERP_G; 27362306a36Sopenharmony_ci shift = ieee80211_vif_get_shift(vif); 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Data frame duration */ 27962306a36Sopenharmony_ci dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, 28062306a36Sopenharmony_ci erp, short_preamble, shift); 28162306a36Sopenharmony_ci if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { 28262306a36Sopenharmony_ci /* ACK duration */ 28362306a36Sopenharmony_ci dur += ieee80211_frame_duration(sband->band, 10, bitrate, 28462306a36Sopenharmony_ci erp, short_preamble, shift); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return cpu_to_le16(dur); 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_ctstoself_duration); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void wake_tx_push_queue(struct ieee80211_local *local, 29262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 29362306a36Sopenharmony_ci struct ieee80211_txq *queue) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct ieee80211_tx_control control = { 29662306a36Sopenharmony_ci .sta = queue->sta, 29762306a36Sopenharmony_ci }; 29862306a36Sopenharmony_ci struct sk_buff *skb; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci while (1) { 30162306a36Sopenharmony_ci skb = ieee80211_tx_dequeue(&local->hw, queue); 30262306a36Sopenharmony_ci if (!skb) 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci drv_tx(local, &control, skb); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* wake_tx_queue handler for driver not implementing a custom one*/ 31062306a36Sopenharmony_civoid ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw, 31162306a36Sopenharmony_ci struct ieee80211_txq *txq) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 31462306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); 31562306a36Sopenharmony_ci struct ieee80211_txq *queue; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci spin_lock(&local->handle_wake_tx_queue_lock); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Use ieee80211_next_txq() for airtime fairness accounting */ 32062306a36Sopenharmony_ci ieee80211_txq_schedule_start(hw, txq->ac); 32162306a36Sopenharmony_ci while ((queue = ieee80211_next_txq(hw, txq->ac))) { 32262306a36Sopenharmony_ci wake_tx_push_queue(local, sdata, queue); 32362306a36Sopenharmony_ci ieee80211_return_txq(hw, queue, false); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci ieee80211_txq_schedule_end(hw, txq->ac); 32662306a36Sopenharmony_ci spin_unlock(&local->handle_wake_tx_queue_lock); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_handle_wake_tx_queue); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 33362306a36Sopenharmony_ci struct ieee80211_vif *vif = &sdata->vif; 33462306a36Sopenharmony_ci struct fq *fq = &local->fq; 33562306a36Sopenharmony_ci struct ps_data *ps = NULL; 33662306a36Sopenharmony_ci struct txq_info *txqi; 33762306a36Sopenharmony_ci struct sta_info *sta; 33862306a36Sopenharmony_ci int i; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci local_bh_disable(); 34162306a36Sopenharmony_ci spin_lock(&fq->lock); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!test_bit(SDATA_STATE_RUNNING, &sdata->state)) 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) 34762306a36Sopenharmony_ci ps = &sdata->bss->ps; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 35062306a36Sopenharmony_ci if (sdata != sta->sdata) 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { 35462306a36Sopenharmony_ci struct ieee80211_txq *txq = sta->sta.txq[i]; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!txq) 35762306a36Sopenharmony_ci continue; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci txqi = to_txq_info(txq); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (ac != txq->ac) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, 36562306a36Sopenharmony_ci &txqi->flags)) 36662306a36Sopenharmony_ci continue; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci spin_unlock(&fq->lock); 36962306a36Sopenharmony_ci drv_wake_tx_queue(local, txqi); 37062306a36Sopenharmony_ci spin_lock(&fq->lock); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!vif->txq) 37562306a36Sopenharmony_ci goto out; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci txqi = to_txq_info(vif->txq); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) || 38062306a36Sopenharmony_ci (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) 38162306a36Sopenharmony_ci goto out; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci spin_unlock(&fq->lock); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci drv_wake_tx_queue(local, txqi); 38662306a36Sopenharmony_ci local_bh_enable(); 38762306a36Sopenharmony_ci return; 38862306a36Sopenharmony_ciout: 38962306a36Sopenharmony_ci spin_unlock(&fq->lock); 39062306a36Sopenharmony_ci local_bh_enable(); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void 39462306a36Sopenharmony_ci__releases(&local->queue_stop_reason_lock) 39562306a36Sopenharmony_ci__acquires(&local->queue_stop_reason_lock) 39662306a36Sopenharmony_ci_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 39962306a36Sopenharmony_ci int n_acs = IEEE80211_NUM_ACS; 40062306a36Sopenharmony_ci int i; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci rcu_read_lock(); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 40562306a36Sopenharmony_ci n_acs = 1; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (i = 0; i < local->hw.queues; i++) { 40862306a36Sopenharmony_ci if (local->queue_stop_reasons[i]) 40962306a36Sopenharmony_ci continue; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags); 41262306a36Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 41362306a36Sopenharmony_ci int ac; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci for (ac = 0; ac < n_acs; ac++) { 41662306a36Sopenharmony_ci int ac_queue = sdata->vif.hw_queue[ac]; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (ac_queue == i || 41962306a36Sopenharmony_ci sdata->vif.cab_queue == i) 42062306a36Sopenharmony_ci __ieee80211_wake_txqs(sdata, ac); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, *flags); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci rcu_read_unlock(); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_civoid ieee80211_wake_txqs(struct tasklet_struct *t) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct ieee80211_local *local = from_tasklet(local, t, 43262306a36Sopenharmony_ci wake_txqs_tasklet); 43362306a36Sopenharmony_ci unsigned long flags; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 43662306a36Sopenharmony_ci _ieee80211_wake_txqs(local, &flags); 43762306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, 44162306a36Sopenharmony_ci enum queue_stop_reason reason, 44262306a36Sopenharmony_ci bool refcounted, 44362306a36Sopenharmony_ci unsigned long *flags) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci trace_wake_queue(local, queue, reason); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (WARN_ON(queue >= hw->queues)) 45062306a36Sopenharmony_ci return; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (!test_bit(reason, &local->queue_stop_reasons[queue])) 45362306a36Sopenharmony_ci return; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!refcounted) { 45662306a36Sopenharmony_ci local->q_stop_reasons[queue][reason] = 0; 45762306a36Sopenharmony_ci } else { 45862306a36Sopenharmony_ci local->q_stop_reasons[queue][reason]--; 45962306a36Sopenharmony_ci if (WARN_ON(local->q_stop_reasons[queue][reason] < 0)) 46062306a36Sopenharmony_ci local->q_stop_reasons[queue][reason] = 0; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (local->q_stop_reasons[queue][reason] == 0) 46462306a36Sopenharmony_ci __clear_bit(reason, &local->queue_stop_reasons[queue]); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (local->queue_stop_reasons[queue] != 0) 46762306a36Sopenharmony_ci /* someone still has this queue stopped */ 46862306a36Sopenharmony_ci return; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!skb_queue_empty(&local->pending[queue])) 47162306a36Sopenharmony_ci tasklet_schedule(&local->tx_pending_tasklet); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Calling _ieee80211_wake_txqs here can be a problem because it may 47562306a36Sopenharmony_ci * release queue_stop_reason_lock which has been taken by 47662306a36Sopenharmony_ci * __ieee80211_wake_queue's caller. It is certainly not very nice to 47762306a36Sopenharmony_ci * release someone's lock, but it is fine because all the callers of 47862306a36Sopenharmony_ci * __ieee80211_wake_queue call it right before releasing the lock. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) 48162306a36Sopenharmony_ci tasklet_schedule(&local->wake_txqs_tasklet); 48262306a36Sopenharmony_ci else 48362306a36Sopenharmony_ci _ieee80211_wake_txqs(local, flags); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_civoid ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, 48762306a36Sopenharmony_ci enum queue_stop_reason reason, 48862306a36Sopenharmony_ci bool refcounted) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 49162306a36Sopenharmony_ci unsigned long flags; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 49462306a36Sopenharmony_ci __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags); 49562306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_civoid ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci ieee80211_wake_queue_by_reason(hw, queue, 50162306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 50262306a36Sopenharmony_ci false); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_wake_queue); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, 50762306a36Sopenharmony_ci enum queue_stop_reason reason, 50862306a36Sopenharmony_ci bool refcounted) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci trace_stop_queue(local, queue, reason); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (WARN_ON(queue >= hw->queues)) 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!refcounted) 51862306a36Sopenharmony_ci local->q_stop_reasons[queue][reason] = 1; 51962306a36Sopenharmony_ci else 52062306a36Sopenharmony_ci local->q_stop_reasons[queue][reason]++; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci set_bit(reason, &local->queue_stop_reasons[queue]); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_civoid ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, 52662306a36Sopenharmony_ci enum queue_stop_reason reason, 52762306a36Sopenharmony_ci bool refcounted) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 53062306a36Sopenharmony_ci unsigned long flags; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 53362306a36Sopenharmony_ci __ieee80211_stop_queue(hw, queue, reason, refcounted); 53462306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_civoid ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci ieee80211_stop_queue_by_reason(hw, queue, 54062306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 54162306a36Sopenharmony_ci false); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_stop_queue); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_civoid ieee80211_add_pending_skb(struct ieee80211_local *local, 54662306a36Sopenharmony_ci struct sk_buff *skb) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct ieee80211_hw *hw = &local->hw; 54962306a36Sopenharmony_ci unsigned long flags; 55062306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 55162306a36Sopenharmony_ci int queue = info->hw_queue; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (WARN_ON(!info->control.vif)) { 55462306a36Sopenharmony_ci ieee80211_free_txskb(&local->hw, skb); 55562306a36Sopenharmony_ci return; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 55962306a36Sopenharmony_ci __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 56062306a36Sopenharmony_ci false); 56162306a36Sopenharmony_ci __skb_queue_tail(&local->pending[queue], skb); 56262306a36Sopenharmony_ci __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 56362306a36Sopenharmony_ci false, &flags); 56462306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_civoid ieee80211_add_pending_skbs(struct ieee80211_local *local, 56862306a36Sopenharmony_ci struct sk_buff_head *skbs) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct ieee80211_hw *hw = &local->hw; 57162306a36Sopenharmony_ci struct sk_buff *skb; 57262306a36Sopenharmony_ci unsigned long flags; 57362306a36Sopenharmony_ci int queue, i; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 57662306a36Sopenharmony_ci while ((skb = skb_dequeue(skbs))) { 57762306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (WARN_ON(!info->control.vif)) { 58062306a36Sopenharmony_ci ieee80211_free_txskb(&local->hw, skb); 58162306a36Sopenharmony_ci continue; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci queue = info->hw_queue; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci __ieee80211_stop_queue(hw, queue, 58762306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 58862306a36Sopenharmony_ci false); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci __skb_queue_tail(&local->pending[queue], skb); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci for (i = 0; i < hw->queues; i++) 59462306a36Sopenharmony_ci __ieee80211_wake_queue(hw, i, 59562306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 59662306a36Sopenharmony_ci false, &flags); 59762306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_civoid ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, 60162306a36Sopenharmony_ci unsigned long queues, 60262306a36Sopenharmony_ci enum queue_stop_reason reason, 60362306a36Sopenharmony_ci bool refcounted) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 60662306a36Sopenharmony_ci unsigned long flags; 60762306a36Sopenharmony_ci int i; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for_each_set_bit(i, &queues, hw->queues) 61262306a36Sopenharmony_ci __ieee80211_stop_queue(hw, i, reason, refcounted); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_civoid ieee80211_stop_queues(struct ieee80211_hw *hw) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 62062306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 62162306a36Sopenharmony_ci false); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_stop_queues); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciint ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 62862306a36Sopenharmony_ci unsigned long flags; 62962306a36Sopenharmony_ci int ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (WARN_ON(queue >= hw->queues)) 63262306a36Sopenharmony_ci return true; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 63562306a36Sopenharmony_ci ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, 63662306a36Sopenharmony_ci &local->queue_stop_reasons[queue]); 63762306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_queue_stopped); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_civoid ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, 64362306a36Sopenharmony_ci unsigned long queues, 64462306a36Sopenharmony_ci enum queue_stop_reason reason, 64562306a36Sopenharmony_ci bool refcounted) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 64862306a36Sopenharmony_ci unsigned long flags; 64962306a36Sopenharmony_ci int i; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci for_each_set_bit(i, &queues, hw->queues) 65462306a36Sopenharmony_ci __ieee80211_wake_queue(hw, i, reason, refcounted, &flags); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_civoid ieee80211_wake_queues(struct ieee80211_hw *hw) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 66262306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 66362306a36Sopenharmony_ci false); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_wake_queues); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic unsigned int 66862306a36Sopenharmony_ciieee80211_get_vif_queues(struct ieee80211_local *local, 66962306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci unsigned int queues; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (sdata && ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) { 67462306a36Sopenharmony_ci int ac; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci queues = 0; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 67962306a36Sopenharmony_ci queues |= BIT(sdata->vif.hw_queue[ac]); 68062306a36Sopenharmony_ci if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) 68162306a36Sopenharmony_ci queues |= BIT(sdata->vif.cab_queue); 68262306a36Sopenharmony_ci } else { 68362306a36Sopenharmony_ci /* all queues */ 68462306a36Sopenharmony_ci queues = BIT(local->hw.queues) - 1; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return queues; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_civoid __ieee80211_flush_queues(struct ieee80211_local *local, 69162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 69262306a36Sopenharmony_ci unsigned int queues, bool drop) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci if (!local->ops->flush) 69562306a36Sopenharmony_ci return; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * If no queue was set, or if the HW doesn't support 69962306a36Sopenharmony_ci * IEEE80211_HW_QUEUE_CONTROL - flush all queues 70062306a36Sopenharmony_ci */ 70162306a36Sopenharmony_ci if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) 70262306a36Sopenharmony_ci queues = ieee80211_get_vif_queues(local, sdata); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci ieee80211_stop_queues_by_reason(&local->hw, queues, 70562306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_FLUSH, 70662306a36Sopenharmony_ci false); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci drv_flush(local, sdata, queues, drop); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ieee80211_wake_queues_by_reason(&local->hw, queues, 71162306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_FLUSH, 71262306a36Sopenharmony_ci false); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_civoid ieee80211_flush_queues(struct ieee80211_local *local, 71662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, bool drop) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci __ieee80211_flush_queues(local, sdata, 0, drop); 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_civoid ieee80211_stop_vif_queues(struct ieee80211_local *local, 72262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 72362306a36Sopenharmony_ci enum queue_stop_reason reason) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci ieee80211_stop_queues_by_reason(&local->hw, 72662306a36Sopenharmony_ci ieee80211_get_vif_queues(local, sdata), 72762306a36Sopenharmony_ci reason, true); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_civoid ieee80211_wake_vif_queues(struct ieee80211_local *local, 73162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 73262306a36Sopenharmony_ci enum queue_stop_reason reason) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci ieee80211_wake_queues_by_reason(&local->hw, 73562306a36Sopenharmony_ci ieee80211_get_vif_queues(local, sdata), 73662306a36Sopenharmony_ci reason, true); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic void __iterate_interfaces(struct ieee80211_local *local, 74062306a36Sopenharmony_ci u32 iter_flags, 74162306a36Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 74262306a36Sopenharmony_ci struct ieee80211_vif *vif), 74362306a36Sopenharmony_ci void *data) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 74662306a36Sopenharmony_ci bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 74962306a36Sopenharmony_ci switch (sdata->vif.type) { 75062306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 75162306a36Sopenharmony_ci if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)) 75262306a36Sopenharmony_ci continue; 75362306a36Sopenharmony_ci break; 75462306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 75562306a36Sopenharmony_ci continue; 75662306a36Sopenharmony_ci default: 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && 76062306a36Sopenharmony_ci active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 76162306a36Sopenharmony_ci continue; 76262306a36Sopenharmony_ci if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) && 76362306a36Sopenharmony_ci !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 76462306a36Sopenharmony_ci continue; 76562306a36Sopenharmony_ci if (ieee80211_sdata_running(sdata) || !active_only) 76662306a36Sopenharmony_ci iterator(data, sdata->vif.addr, 76762306a36Sopenharmony_ci &sdata->vif); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci sdata = rcu_dereference_check(local->monitor_sdata, 77162306a36Sopenharmony_ci lockdep_is_held(&local->iflist_mtx) || 77262306a36Sopenharmony_ci lockdep_is_held(&local->hw.wiphy->mtx)); 77362306a36Sopenharmony_ci if (sdata && 77462306a36Sopenharmony_ci (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only || 77562306a36Sopenharmony_ci sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 77662306a36Sopenharmony_ci iterator(data, sdata->vif.addr, &sdata->vif); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_civoid ieee80211_iterate_interfaces( 78062306a36Sopenharmony_ci struct ieee80211_hw *hw, u32 iter_flags, 78162306a36Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 78262306a36Sopenharmony_ci struct ieee80211_vif *vif), 78362306a36Sopenharmony_ci void *data) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci mutex_lock(&local->iflist_mtx); 78862306a36Sopenharmony_ci __iterate_interfaces(local, iter_flags, iterator, data); 78962306a36Sopenharmony_ci mutex_unlock(&local->iflist_mtx); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_civoid ieee80211_iterate_active_interfaces_atomic( 79462306a36Sopenharmony_ci struct ieee80211_hw *hw, u32 iter_flags, 79562306a36Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 79662306a36Sopenharmony_ci struct ieee80211_vif *vif), 79762306a36Sopenharmony_ci void *data) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci rcu_read_lock(); 80262306a36Sopenharmony_ci __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, 80362306a36Sopenharmony_ci iterator, data); 80462306a36Sopenharmony_ci rcu_read_unlock(); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_civoid ieee80211_iterate_active_interfaces_mtx( 80962306a36Sopenharmony_ci struct ieee80211_hw *hw, u32 iter_flags, 81062306a36Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 81162306a36Sopenharmony_ci struct ieee80211_vif *vif), 81262306a36Sopenharmony_ci void *data) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci lockdep_assert_wiphy(hw->wiphy); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, 81962306a36Sopenharmony_ci iterator, data); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_mtx); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void __iterate_stations(struct ieee80211_local *local, 82462306a36Sopenharmony_ci void (*iterator)(void *data, 82562306a36Sopenharmony_ci struct ieee80211_sta *sta), 82662306a36Sopenharmony_ci void *data) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct sta_info *sta; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 83162306a36Sopenharmony_ci if (!sta->uploaded) 83262306a36Sopenharmony_ci continue; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci iterator(data, &sta->sta); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_civoid ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, 83962306a36Sopenharmony_ci void (*iterator)(void *data, 84062306a36Sopenharmony_ci struct ieee80211_sta *sta), 84162306a36Sopenharmony_ci void *data) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci rcu_read_lock(); 84662306a36Sopenharmony_ci __iterate_stations(local, iterator, data); 84762306a36Sopenharmony_ci rcu_read_unlock(); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistruct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata) || 85662306a36Sopenharmony_ci !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 85762306a36Sopenharmony_ci return NULL; 85862306a36Sopenharmony_ci return &sdata->vif; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistruct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci if (!vif) 86562306a36Sopenharmony_ci return NULL; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return &vif_to_sdata(vif)->wdev; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci/* 87262306a36Sopenharmony_ci * Nothing should have been stuffed into the workqueue during 87362306a36Sopenharmony_ci * the suspend->resume cycle. Since we can't check each caller 87462306a36Sopenharmony_ci * of this function if we are already quiescing / suspended, 87562306a36Sopenharmony_ci * check here and don't WARN since this can actually happen when 87662306a36Sopenharmony_ci * the rx path (for example) is racing against __ieee80211_suspend 87762306a36Sopenharmony_ci * and suspending / quiescing was set after the rx path checked 87862306a36Sopenharmony_ci * them. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_cistatic bool ieee80211_can_queue_work(struct ieee80211_local *local) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci if (local->quiescing || (local->suspended && !local->resuming)) { 88362306a36Sopenharmony_ci pr_warn("queueing ieee80211 work while going to suspend\n"); 88462306a36Sopenharmony_ci return false; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return true; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_civoid ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (!ieee80211_can_queue_work(local)) 89562306a36Sopenharmony_ci return; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci queue_work(local->workqueue, work); 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_queue_work); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_civoid ieee80211_queue_delayed_work(struct ieee80211_hw *hw, 90262306a36Sopenharmony_ci struct delayed_work *dwork, 90362306a36Sopenharmony_ci unsigned long delay) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (!ieee80211_can_queue_work(local)) 90862306a36Sopenharmony_ci return; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci queue_delayed_work(local->workqueue, dwork, delay); 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_queue_delayed_work); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic void 91562306a36Sopenharmony_ciieee80211_parse_extension_element(u32 *crc, 91662306a36Sopenharmony_ci const struct element *elem, 91762306a36Sopenharmony_ci struct ieee802_11_elems *elems, 91862306a36Sopenharmony_ci struct ieee80211_elems_parse_params *params) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci const void *data = elem->data + 1; 92162306a36Sopenharmony_ci bool calc_crc = false; 92262306a36Sopenharmony_ci u8 len; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!elem->datalen) 92562306a36Sopenharmony_ci return; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci len = elem->datalen - 1; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci switch (elem->data[0]) { 93062306a36Sopenharmony_ci case WLAN_EID_EXT_HE_MU_EDCA: 93162306a36Sopenharmony_ci calc_crc = true; 93262306a36Sopenharmony_ci if (len >= sizeof(*elems->mu_edca_param_set)) 93362306a36Sopenharmony_ci elems->mu_edca_param_set = data; 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci case WLAN_EID_EXT_HE_CAPABILITY: 93662306a36Sopenharmony_ci if (ieee80211_he_capa_size_ok(data, len)) { 93762306a36Sopenharmony_ci elems->he_cap = data; 93862306a36Sopenharmony_ci elems->he_cap_len = len; 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci case WLAN_EID_EXT_HE_OPERATION: 94262306a36Sopenharmony_ci calc_crc = true; 94362306a36Sopenharmony_ci if (len >= sizeof(*elems->he_operation) && 94462306a36Sopenharmony_ci len >= ieee80211_he_oper_size(data) - 1) 94562306a36Sopenharmony_ci elems->he_operation = data; 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case WLAN_EID_EXT_UORA: 94862306a36Sopenharmony_ci if (len >= 1) 94962306a36Sopenharmony_ci elems->uora_element = data; 95062306a36Sopenharmony_ci break; 95162306a36Sopenharmony_ci case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: 95262306a36Sopenharmony_ci if (len == 3) 95362306a36Sopenharmony_ci elems->max_channel_switch_time = data; 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: 95662306a36Sopenharmony_ci if (len >= sizeof(*elems->mbssid_config_ie)) 95762306a36Sopenharmony_ci elems->mbssid_config_ie = data; 95862306a36Sopenharmony_ci break; 95962306a36Sopenharmony_ci case WLAN_EID_EXT_HE_SPR: 96062306a36Sopenharmony_ci if (len >= sizeof(*elems->he_spr) && 96162306a36Sopenharmony_ci len >= ieee80211_he_spr_size(data)) 96262306a36Sopenharmony_ci elems->he_spr = data; 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci case WLAN_EID_EXT_HE_6GHZ_CAPA: 96562306a36Sopenharmony_ci if (len >= sizeof(*elems->he_6ghz_capa)) 96662306a36Sopenharmony_ci elems->he_6ghz_capa = data; 96762306a36Sopenharmony_ci break; 96862306a36Sopenharmony_ci case WLAN_EID_EXT_EHT_CAPABILITY: 96962306a36Sopenharmony_ci if (ieee80211_eht_capa_size_ok(elems->he_cap, 97062306a36Sopenharmony_ci data, len, 97162306a36Sopenharmony_ci params->from_ap)) { 97262306a36Sopenharmony_ci elems->eht_cap = data; 97362306a36Sopenharmony_ci elems->eht_cap_len = len; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci case WLAN_EID_EXT_EHT_OPERATION: 97762306a36Sopenharmony_ci if (ieee80211_eht_oper_size_ok(data, len)) 97862306a36Sopenharmony_ci elems->eht_operation = data; 97962306a36Sopenharmony_ci calc_crc = true; 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci case WLAN_EID_EXT_EHT_MULTI_LINK: 98262306a36Sopenharmony_ci calc_crc = true; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (ieee80211_mle_size_ok(data, len)) { 98562306a36Sopenharmony_ci const struct ieee80211_multi_link_elem *mle = 98662306a36Sopenharmony_ci (void *)data; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci switch (le16_get_bits(mle->control, 98962306a36Sopenharmony_ci IEEE80211_ML_CONTROL_TYPE)) { 99062306a36Sopenharmony_ci case IEEE80211_ML_CONTROL_TYPE_BASIC: 99162306a36Sopenharmony_ci elems->ml_basic_elem = (void *)elem; 99262306a36Sopenharmony_ci elems->ml_basic = data; 99362306a36Sopenharmony_ci elems->ml_basic_len = len; 99462306a36Sopenharmony_ci break; 99562306a36Sopenharmony_ci case IEEE80211_ML_CONTROL_TYPE_RECONF: 99662306a36Sopenharmony_ci elems->ml_reconf_elem = (void *)elem; 99762306a36Sopenharmony_ci elems->ml_reconf = data; 99862306a36Sopenharmony_ci elems->ml_reconf_len = len; 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci default: 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (crc && calc_crc) 100862306a36Sopenharmony_ci *crc = crc32_be(*crc, (void *)elem, elem->datalen + 2); 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic u32 101262306a36Sopenharmony_ci_ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params, 101362306a36Sopenharmony_ci struct ieee802_11_elems *elems, 101462306a36Sopenharmony_ci const struct element *check_inherit) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci const struct element *elem; 101762306a36Sopenharmony_ci bool calc_crc = params->filter != 0; 101862306a36Sopenharmony_ci DECLARE_BITMAP(seen_elems, 256); 101962306a36Sopenharmony_ci u32 crc = params->crc; 102062306a36Sopenharmony_ci const u8 *ie; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci bitmap_zero(seen_elems, 256); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci for_each_element(elem, params->start, params->len) { 102562306a36Sopenharmony_ci bool elem_parse_failed; 102662306a36Sopenharmony_ci u8 id = elem->id; 102762306a36Sopenharmony_ci u8 elen = elem->datalen; 102862306a36Sopenharmony_ci const u8 *pos = elem->data; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (check_inherit && 103162306a36Sopenharmony_ci !cfg80211_is_element_inherited(elem, 103262306a36Sopenharmony_ci check_inherit)) 103362306a36Sopenharmony_ci continue; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci switch (id) { 103662306a36Sopenharmony_ci case WLAN_EID_SSID: 103762306a36Sopenharmony_ci case WLAN_EID_SUPP_RATES: 103862306a36Sopenharmony_ci case WLAN_EID_FH_PARAMS: 103962306a36Sopenharmony_ci case WLAN_EID_DS_PARAMS: 104062306a36Sopenharmony_ci case WLAN_EID_CF_PARAMS: 104162306a36Sopenharmony_ci case WLAN_EID_TIM: 104262306a36Sopenharmony_ci case WLAN_EID_IBSS_PARAMS: 104362306a36Sopenharmony_ci case WLAN_EID_CHALLENGE: 104462306a36Sopenharmony_ci case WLAN_EID_RSN: 104562306a36Sopenharmony_ci case WLAN_EID_ERP_INFO: 104662306a36Sopenharmony_ci case WLAN_EID_EXT_SUPP_RATES: 104762306a36Sopenharmony_ci case WLAN_EID_HT_CAPABILITY: 104862306a36Sopenharmony_ci case WLAN_EID_HT_OPERATION: 104962306a36Sopenharmony_ci case WLAN_EID_VHT_CAPABILITY: 105062306a36Sopenharmony_ci case WLAN_EID_VHT_OPERATION: 105162306a36Sopenharmony_ci case WLAN_EID_MESH_ID: 105262306a36Sopenharmony_ci case WLAN_EID_MESH_CONFIG: 105362306a36Sopenharmony_ci case WLAN_EID_PEER_MGMT: 105462306a36Sopenharmony_ci case WLAN_EID_PREQ: 105562306a36Sopenharmony_ci case WLAN_EID_PREP: 105662306a36Sopenharmony_ci case WLAN_EID_PERR: 105762306a36Sopenharmony_ci case WLAN_EID_RANN: 105862306a36Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH: 105962306a36Sopenharmony_ci case WLAN_EID_EXT_CHANSWITCH_ANN: 106062306a36Sopenharmony_ci case WLAN_EID_COUNTRY: 106162306a36Sopenharmony_ci case WLAN_EID_PWR_CONSTRAINT: 106262306a36Sopenharmony_ci case WLAN_EID_TIMEOUT_INTERVAL: 106362306a36Sopenharmony_ci case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 106462306a36Sopenharmony_ci case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 106562306a36Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_PARAM: 106662306a36Sopenharmony_ci case WLAN_EID_EXT_CAPABILITY: 106762306a36Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_TIMING: 106862306a36Sopenharmony_ci case WLAN_EID_LINK_ID: 106962306a36Sopenharmony_ci case WLAN_EID_BSS_MAX_IDLE_PERIOD: 107062306a36Sopenharmony_ci case WLAN_EID_RSNX: 107162306a36Sopenharmony_ci case WLAN_EID_S1G_BCN_COMPAT: 107262306a36Sopenharmony_ci case WLAN_EID_S1G_CAPABILITIES: 107362306a36Sopenharmony_ci case WLAN_EID_S1G_OPERATION: 107462306a36Sopenharmony_ci case WLAN_EID_AID_RESPONSE: 107562306a36Sopenharmony_ci case WLAN_EID_S1G_SHORT_BCN_INTERVAL: 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 107862306a36Sopenharmony_ci * that if the content gets bigger it might be needed more than once 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_ci if (test_bit(id, seen_elems)) { 108162306a36Sopenharmony_ci elems->parse_error = true; 108262306a36Sopenharmony_ci continue; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci break; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (calc_crc && id < 64 && (params->filter & (1ULL << id))) 108862306a36Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci elem_parse_failed = false; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci switch (id) { 109362306a36Sopenharmony_ci case WLAN_EID_LINK_ID: 109462306a36Sopenharmony_ci if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { 109562306a36Sopenharmony_ci elem_parse_failed = true; 109662306a36Sopenharmony_ci break; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci elems->lnk_id = (void *)(pos - 2); 109962306a36Sopenharmony_ci break; 110062306a36Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_TIMING: 110162306a36Sopenharmony_ci if (elen < sizeof(struct ieee80211_ch_switch_timing)) { 110262306a36Sopenharmony_ci elem_parse_failed = true; 110362306a36Sopenharmony_ci break; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci elems->ch_sw_timing = (void *)pos; 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci case WLAN_EID_EXT_CAPABILITY: 110862306a36Sopenharmony_ci elems->ext_capab = pos; 110962306a36Sopenharmony_ci elems->ext_capab_len = elen; 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci case WLAN_EID_SSID: 111262306a36Sopenharmony_ci elems->ssid = pos; 111362306a36Sopenharmony_ci elems->ssid_len = elen; 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci case WLAN_EID_SUPP_RATES: 111662306a36Sopenharmony_ci elems->supp_rates = pos; 111762306a36Sopenharmony_ci elems->supp_rates_len = elen; 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci case WLAN_EID_DS_PARAMS: 112062306a36Sopenharmony_ci if (elen >= 1) 112162306a36Sopenharmony_ci elems->ds_params = pos; 112262306a36Sopenharmony_ci else 112362306a36Sopenharmony_ci elem_parse_failed = true; 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci case WLAN_EID_TIM: 112662306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_tim_ie)) { 112762306a36Sopenharmony_ci elems->tim = (void *)pos; 112862306a36Sopenharmony_ci elems->tim_len = elen; 112962306a36Sopenharmony_ci } else 113062306a36Sopenharmony_ci elem_parse_failed = true; 113162306a36Sopenharmony_ci break; 113262306a36Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 113362306a36Sopenharmony_ci if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && 113462306a36Sopenharmony_ci pos[2] == 0xf2) { 113562306a36Sopenharmony_ci /* Microsoft OUI (00:50:F2) */ 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (calc_crc) 113862306a36Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (elen >= 5 && pos[3] == 2) { 114162306a36Sopenharmony_ci /* OUI Type 2 - WMM IE */ 114262306a36Sopenharmony_ci if (pos[4] == 0) { 114362306a36Sopenharmony_ci elems->wmm_info = pos; 114462306a36Sopenharmony_ci elems->wmm_info_len = elen; 114562306a36Sopenharmony_ci } else if (pos[4] == 1) { 114662306a36Sopenharmony_ci elems->wmm_param = pos; 114762306a36Sopenharmony_ci elems->wmm_param_len = elen; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci case WLAN_EID_RSN: 115362306a36Sopenharmony_ci elems->rsn = pos; 115462306a36Sopenharmony_ci elems->rsn_len = elen; 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci case WLAN_EID_ERP_INFO: 115762306a36Sopenharmony_ci if (elen >= 1) 115862306a36Sopenharmony_ci elems->erp_info = pos; 115962306a36Sopenharmony_ci else 116062306a36Sopenharmony_ci elem_parse_failed = true; 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci case WLAN_EID_EXT_SUPP_RATES: 116362306a36Sopenharmony_ci elems->ext_supp_rates = pos; 116462306a36Sopenharmony_ci elems->ext_supp_rates_len = elen; 116562306a36Sopenharmony_ci break; 116662306a36Sopenharmony_ci case WLAN_EID_HT_CAPABILITY: 116762306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_ht_cap)) 116862306a36Sopenharmony_ci elems->ht_cap_elem = (void *)pos; 116962306a36Sopenharmony_ci else 117062306a36Sopenharmony_ci elem_parse_failed = true; 117162306a36Sopenharmony_ci break; 117262306a36Sopenharmony_ci case WLAN_EID_HT_OPERATION: 117362306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_ht_operation)) 117462306a36Sopenharmony_ci elems->ht_operation = (void *)pos; 117562306a36Sopenharmony_ci else 117662306a36Sopenharmony_ci elem_parse_failed = true; 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci case WLAN_EID_VHT_CAPABILITY: 117962306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_vht_cap)) 118062306a36Sopenharmony_ci elems->vht_cap_elem = (void *)pos; 118162306a36Sopenharmony_ci else 118262306a36Sopenharmony_ci elem_parse_failed = true; 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci case WLAN_EID_VHT_OPERATION: 118562306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_vht_operation)) { 118662306a36Sopenharmony_ci elems->vht_operation = (void *)pos; 118762306a36Sopenharmony_ci if (calc_crc) 118862306a36Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 118962306a36Sopenharmony_ci break; 119062306a36Sopenharmony_ci } 119162306a36Sopenharmony_ci elem_parse_failed = true; 119262306a36Sopenharmony_ci break; 119362306a36Sopenharmony_ci case WLAN_EID_OPMODE_NOTIF: 119462306a36Sopenharmony_ci if (elen > 0) { 119562306a36Sopenharmony_ci elems->opmode_notif = pos; 119662306a36Sopenharmony_ci if (calc_crc) 119762306a36Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 119862306a36Sopenharmony_ci break; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci elem_parse_failed = true; 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci case WLAN_EID_MESH_ID: 120362306a36Sopenharmony_ci elems->mesh_id = pos; 120462306a36Sopenharmony_ci elems->mesh_id_len = elen; 120562306a36Sopenharmony_ci break; 120662306a36Sopenharmony_ci case WLAN_EID_MESH_CONFIG: 120762306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_meshconf_ie)) 120862306a36Sopenharmony_ci elems->mesh_config = (void *)pos; 120962306a36Sopenharmony_ci else 121062306a36Sopenharmony_ci elem_parse_failed = true; 121162306a36Sopenharmony_ci break; 121262306a36Sopenharmony_ci case WLAN_EID_PEER_MGMT: 121362306a36Sopenharmony_ci elems->peering = pos; 121462306a36Sopenharmony_ci elems->peering_len = elen; 121562306a36Sopenharmony_ci break; 121662306a36Sopenharmony_ci case WLAN_EID_MESH_AWAKE_WINDOW: 121762306a36Sopenharmony_ci if (elen >= 2) 121862306a36Sopenharmony_ci elems->awake_window = (void *)pos; 121962306a36Sopenharmony_ci break; 122062306a36Sopenharmony_ci case WLAN_EID_PREQ: 122162306a36Sopenharmony_ci elems->preq = pos; 122262306a36Sopenharmony_ci elems->preq_len = elen; 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci case WLAN_EID_PREP: 122562306a36Sopenharmony_ci elems->prep = pos; 122662306a36Sopenharmony_ci elems->prep_len = elen; 122762306a36Sopenharmony_ci break; 122862306a36Sopenharmony_ci case WLAN_EID_PERR: 122962306a36Sopenharmony_ci elems->perr = pos; 123062306a36Sopenharmony_ci elems->perr_len = elen; 123162306a36Sopenharmony_ci break; 123262306a36Sopenharmony_ci case WLAN_EID_RANN: 123362306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_rann_ie)) 123462306a36Sopenharmony_ci elems->rann = (void *)pos; 123562306a36Sopenharmony_ci else 123662306a36Sopenharmony_ci elem_parse_failed = true; 123762306a36Sopenharmony_ci break; 123862306a36Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH: 123962306a36Sopenharmony_ci if (elen != sizeof(struct ieee80211_channel_sw_ie)) { 124062306a36Sopenharmony_ci elem_parse_failed = true; 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci elems->ch_switch_ie = (void *)pos; 124462306a36Sopenharmony_ci break; 124562306a36Sopenharmony_ci case WLAN_EID_EXT_CHANSWITCH_ANN: 124662306a36Sopenharmony_ci if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { 124762306a36Sopenharmony_ci elem_parse_failed = true; 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci elems->ext_chansw_ie = (void *)pos; 125162306a36Sopenharmony_ci break; 125262306a36Sopenharmony_ci case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 125362306a36Sopenharmony_ci if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { 125462306a36Sopenharmony_ci elem_parse_failed = true; 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci elems->sec_chan_offs = (void *)pos; 125862306a36Sopenharmony_ci break; 125962306a36Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_PARAM: 126062306a36Sopenharmony_ci if (elen < 126162306a36Sopenharmony_ci sizeof(*elems->mesh_chansw_params_ie)) { 126262306a36Sopenharmony_ci elem_parse_failed = true; 126362306a36Sopenharmony_ci break; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci elems->mesh_chansw_params_ie = (void *)pos; 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 126862306a36Sopenharmony_ci if (!params->action || 126962306a36Sopenharmony_ci elen < sizeof(*elems->wide_bw_chansw_ie)) { 127062306a36Sopenharmony_ci elem_parse_failed = true; 127162306a36Sopenharmony_ci break; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci elems->wide_bw_chansw_ie = (void *)pos; 127462306a36Sopenharmony_ci break; 127562306a36Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH_WRAPPER: 127662306a36Sopenharmony_ci if (params->action) { 127762306a36Sopenharmony_ci elem_parse_failed = true; 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci /* 128162306a36Sopenharmony_ci * This is a bit tricky, but as we only care about 128262306a36Sopenharmony_ci * the wide bandwidth channel switch element, so 128362306a36Sopenharmony_ci * just parse it out manually. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, 128662306a36Sopenharmony_ci pos, elen); 128762306a36Sopenharmony_ci if (ie) { 128862306a36Sopenharmony_ci if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie)) 128962306a36Sopenharmony_ci elems->wide_bw_chansw_ie = 129062306a36Sopenharmony_ci (void *)(ie + 2); 129162306a36Sopenharmony_ci else 129262306a36Sopenharmony_ci elem_parse_failed = true; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci break; 129562306a36Sopenharmony_ci case WLAN_EID_COUNTRY: 129662306a36Sopenharmony_ci elems->country_elem = pos; 129762306a36Sopenharmony_ci elems->country_elem_len = elen; 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci case WLAN_EID_PWR_CONSTRAINT: 130062306a36Sopenharmony_ci if (elen != 1) { 130162306a36Sopenharmony_ci elem_parse_failed = true; 130262306a36Sopenharmony_ci break; 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci elems->pwr_constr_elem = pos; 130562306a36Sopenharmony_ci break; 130662306a36Sopenharmony_ci case WLAN_EID_CISCO_VENDOR_SPECIFIC: 130762306a36Sopenharmony_ci /* Lots of different options exist, but we only care 130862306a36Sopenharmony_ci * about the Dynamic Transmit Power Control element. 130962306a36Sopenharmony_ci * First check for the Cisco OUI, then for the DTPC 131062306a36Sopenharmony_ci * tag (0x00). 131162306a36Sopenharmony_ci */ 131262306a36Sopenharmony_ci if (elen < 4) { 131362306a36Sopenharmony_ci elem_parse_failed = true; 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (pos[0] != 0x00 || pos[1] != 0x40 || 131862306a36Sopenharmony_ci pos[2] != 0x96 || pos[3] != 0x00) 131962306a36Sopenharmony_ci break; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (elen != 6) { 132262306a36Sopenharmony_ci elem_parse_failed = true; 132362306a36Sopenharmony_ci break; 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci if (calc_crc) 132762306a36Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci elems->cisco_dtpc_elem = pos; 133062306a36Sopenharmony_ci break; 133162306a36Sopenharmony_ci case WLAN_EID_ADDBA_EXT: 133262306a36Sopenharmony_ci if (elen < sizeof(struct ieee80211_addba_ext_ie)) { 133362306a36Sopenharmony_ci elem_parse_failed = true; 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci elems->addba_ext_ie = (void *)pos; 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci case WLAN_EID_TIMEOUT_INTERVAL: 133962306a36Sopenharmony_ci if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) 134062306a36Sopenharmony_ci elems->timeout_int = (void *)pos; 134162306a36Sopenharmony_ci else 134262306a36Sopenharmony_ci elem_parse_failed = true; 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci case WLAN_EID_BSS_MAX_IDLE_PERIOD: 134562306a36Sopenharmony_ci if (elen >= sizeof(*elems->max_idle_period_ie)) 134662306a36Sopenharmony_ci elems->max_idle_period_ie = (void *)pos; 134762306a36Sopenharmony_ci break; 134862306a36Sopenharmony_ci case WLAN_EID_RSNX: 134962306a36Sopenharmony_ci elems->rsnx = pos; 135062306a36Sopenharmony_ci elems->rsnx_len = elen; 135162306a36Sopenharmony_ci break; 135262306a36Sopenharmony_ci case WLAN_EID_TX_POWER_ENVELOPE: 135362306a36Sopenharmony_ci if (elen < 1 || 135462306a36Sopenharmony_ci elen > sizeof(struct ieee80211_tx_pwr_env)) 135562306a36Sopenharmony_ci break; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (elems->tx_pwr_env_num >= ARRAY_SIZE(elems->tx_pwr_env)) 135862306a36Sopenharmony_ci break; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci elems->tx_pwr_env[elems->tx_pwr_env_num] = (void *)pos; 136162306a36Sopenharmony_ci elems->tx_pwr_env_len[elems->tx_pwr_env_num] = elen; 136262306a36Sopenharmony_ci elems->tx_pwr_env_num++; 136362306a36Sopenharmony_ci break; 136462306a36Sopenharmony_ci case WLAN_EID_EXTENSION: 136562306a36Sopenharmony_ci ieee80211_parse_extension_element(calc_crc ? 136662306a36Sopenharmony_ci &crc : NULL, 136762306a36Sopenharmony_ci elem, elems, params); 136862306a36Sopenharmony_ci break; 136962306a36Sopenharmony_ci case WLAN_EID_S1G_CAPABILITIES: 137062306a36Sopenharmony_ci if (elen >= sizeof(*elems->s1g_capab)) 137162306a36Sopenharmony_ci elems->s1g_capab = (void *)pos; 137262306a36Sopenharmony_ci else 137362306a36Sopenharmony_ci elem_parse_failed = true; 137462306a36Sopenharmony_ci break; 137562306a36Sopenharmony_ci case WLAN_EID_S1G_OPERATION: 137662306a36Sopenharmony_ci if (elen == sizeof(*elems->s1g_oper)) 137762306a36Sopenharmony_ci elems->s1g_oper = (void *)pos; 137862306a36Sopenharmony_ci else 137962306a36Sopenharmony_ci elem_parse_failed = true; 138062306a36Sopenharmony_ci break; 138162306a36Sopenharmony_ci case WLAN_EID_S1G_BCN_COMPAT: 138262306a36Sopenharmony_ci if (elen == sizeof(*elems->s1g_bcn_compat)) 138362306a36Sopenharmony_ci elems->s1g_bcn_compat = (void *)pos; 138462306a36Sopenharmony_ci else 138562306a36Sopenharmony_ci elem_parse_failed = true; 138662306a36Sopenharmony_ci break; 138762306a36Sopenharmony_ci case WLAN_EID_AID_RESPONSE: 138862306a36Sopenharmony_ci if (elen == sizeof(struct ieee80211_aid_response_ie)) 138962306a36Sopenharmony_ci elems->aid_resp = (void *)pos; 139062306a36Sopenharmony_ci else 139162306a36Sopenharmony_ci elem_parse_failed = true; 139262306a36Sopenharmony_ci break; 139362306a36Sopenharmony_ci default: 139462306a36Sopenharmony_ci break; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci if (elem_parse_failed) 139862306a36Sopenharmony_ci elems->parse_error = true; 139962306a36Sopenharmony_ci else 140062306a36Sopenharmony_ci __set_bit(id, seen_elems); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci if (!for_each_element_completed(elem, params->start, params->len)) 140462306a36Sopenharmony_ci elems->parse_error = true; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci return crc; 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, 141062306a36Sopenharmony_ci struct ieee802_11_elems *elems, 141162306a36Sopenharmony_ci struct cfg80211_bss *bss, 141262306a36Sopenharmony_ci u8 *nontransmitted_profile) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci const struct element *elem, *sub; 141562306a36Sopenharmony_ci size_t profile_len = 0; 141662306a36Sopenharmony_ci bool found = false; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (!bss || !bss->transmitted_bss) 141962306a36Sopenharmony_ci return profile_len; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { 142262306a36Sopenharmony_ci if (elem->datalen < 2) 142362306a36Sopenharmony_ci continue; 142462306a36Sopenharmony_ci if (elem->data[0] < 1 || elem->data[0] > 8) 142562306a36Sopenharmony_ci continue; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci for_each_element(sub, elem->data + 1, elem->datalen - 1) { 142862306a36Sopenharmony_ci u8 new_bssid[ETH_ALEN]; 142962306a36Sopenharmony_ci const u8 *index; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (sub->id != 0 || sub->datalen < 4) { 143262306a36Sopenharmony_ci /* not a valid BSS profile */ 143362306a36Sopenharmony_ci continue; 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || 143762306a36Sopenharmony_ci sub->data[1] != 2) { 143862306a36Sopenharmony_ci /* The first element of the 143962306a36Sopenharmony_ci * Nontransmitted BSSID Profile is not 144062306a36Sopenharmony_ci * the Nontransmitted BSSID Capability 144162306a36Sopenharmony_ci * element. 144262306a36Sopenharmony_ci */ 144362306a36Sopenharmony_ci continue; 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci memset(nontransmitted_profile, 0, len); 144762306a36Sopenharmony_ci profile_len = cfg80211_merge_profile(start, len, 144862306a36Sopenharmony_ci elem, 144962306a36Sopenharmony_ci sub, 145062306a36Sopenharmony_ci nontransmitted_profile, 145162306a36Sopenharmony_ci len); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* found a Nontransmitted BSSID Profile */ 145462306a36Sopenharmony_ci index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, 145562306a36Sopenharmony_ci nontransmitted_profile, 145662306a36Sopenharmony_ci profile_len); 145762306a36Sopenharmony_ci if (!index || index[1] < 1 || index[2] == 0) { 145862306a36Sopenharmony_ci /* Invalid MBSSID Index element */ 145962306a36Sopenharmony_ci continue; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci cfg80211_gen_new_bssid(bss->transmitted_bss->bssid, 146362306a36Sopenharmony_ci elem->data[0], 146462306a36Sopenharmony_ci index[2], 146562306a36Sopenharmony_ci new_bssid); 146662306a36Sopenharmony_ci if (ether_addr_equal(new_bssid, bss->bssid)) { 146762306a36Sopenharmony_ci found = true; 146862306a36Sopenharmony_ci elems->bssid_index_len = index[1]; 146962306a36Sopenharmony_ci elems->bssid_index = (void *)&index[2]; 147062306a36Sopenharmony_ci break; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci return found ? profile_len : 0; 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic void ieee80211_mle_get_sta_prof(struct ieee802_11_elems *elems, 147962306a36Sopenharmony_ci u8 link_id) 148062306a36Sopenharmony_ci{ 148162306a36Sopenharmony_ci const struct ieee80211_multi_link_elem *ml = elems->ml_basic; 148262306a36Sopenharmony_ci ssize_t ml_len = elems->ml_basic_len; 148362306a36Sopenharmony_ci const struct element *sub; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (!ml || !ml_len) 148662306a36Sopenharmony_ci return; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (le16_get_bits(ml->control, IEEE80211_ML_CONTROL_TYPE) != 148962306a36Sopenharmony_ci IEEE80211_ML_CONTROL_TYPE_BASIC) 149062306a36Sopenharmony_ci return; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci for_each_mle_subelement(sub, (u8 *)ml, ml_len) { 149362306a36Sopenharmony_ci struct ieee80211_mle_per_sta_profile *prof = (void *)sub->data; 149462306a36Sopenharmony_ci ssize_t sta_prof_len; 149562306a36Sopenharmony_ci u16 control; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci if (sub->id != IEEE80211_MLE_SUBELEM_PER_STA_PROFILE) 149862306a36Sopenharmony_ci continue; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (!ieee80211_mle_basic_sta_prof_size_ok(sub->data, 150162306a36Sopenharmony_ci sub->datalen)) 150262306a36Sopenharmony_ci return; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci control = le16_to_cpu(prof->control); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (link_id != u16_get_bits(control, 150762306a36Sopenharmony_ci IEEE80211_MLE_STA_CONTROL_LINK_ID)) 150862306a36Sopenharmony_ci continue; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE)) 151162306a36Sopenharmony_ci return; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci /* the sub element can be fragmented */ 151462306a36Sopenharmony_ci sta_prof_len = 151562306a36Sopenharmony_ci cfg80211_defragment_element(sub, 151662306a36Sopenharmony_ci (u8 *)ml, ml_len, 151762306a36Sopenharmony_ci elems->scratch_pos, 151862306a36Sopenharmony_ci elems->scratch + 151962306a36Sopenharmony_ci elems->scratch_len - 152062306a36Sopenharmony_ci elems->scratch_pos, 152162306a36Sopenharmony_ci IEEE80211_MLE_SUBELEM_FRAGMENT); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (sta_prof_len < 0) 152462306a36Sopenharmony_ci return; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci elems->prof = (void *)elems->scratch_pos; 152762306a36Sopenharmony_ci elems->sta_prof_len = sta_prof_len; 152862306a36Sopenharmony_ci elems->scratch_pos += sta_prof_len; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci return; 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci} 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_cistatic void ieee80211_mle_parse_link(struct ieee802_11_elems *elems, 153562306a36Sopenharmony_ci struct ieee80211_elems_parse_params *params) 153662306a36Sopenharmony_ci{ 153762306a36Sopenharmony_ci struct ieee80211_mle_per_sta_profile *prof; 153862306a36Sopenharmony_ci struct ieee80211_elems_parse_params sub = { 153962306a36Sopenharmony_ci .action = params->action, 154062306a36Sopenharmony_ci .from_ap = params->from_ap, 154162306a36Sopenharmony_ci .link_id = -1, 154262306a36Sopenharmony_ci }; 154362306a36Sopenharmony_ci ssize_t ml_len = elems->ml_basic_len; 154462306a36Sopenharmony_ci const struct element *non_inherit = NULL; 154562306a36Sopenharmony_ci const u8 *end; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (params->link_id == -1) 154862306a36Sopenharmony_ci return; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci ml_len = cfg80211_defragment_element(elems->ml_basic_elem, 155162306a36Sopenharmony_ci elems->ie_start, 155262306a36Sopenharmony_ci elems->total_len, 155362306a36Sopenharmony_ci elems->scratch_pos, 155462306a36Sopenharmony_ci elems->scratch + 155562306a36Sopenharmony_ci elems->scratch_len - 155662306a36Sopenharmony_ci elems->scratch_pos, 155762306a36Sopenharmony_ci WLAN_EID_FRAGMENT); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (ml_len < 0) 156062306a36Sopenharmony_ci return; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci elems->ml_basic = (const void *)elems->scratch_pos; 156362306a36Sopenharmony_ci elems->ml_basic_len = ml_len; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci ieee80211_mle_get_sta_prof(elems, params->link_id); 156662306a36Sopenharmony_ci prof = elems->prof; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (!prof) 156962306a36Sopenharmony_ci return; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* check if we have the 4 bytes for the fixed part in assoc response */ 157262306a36Sopenharmony_ci if (elems->sta_prof_len < sizeof(*prof) + prof->sta_info_len - 1 + 4) { 157362306a36Sopenharmony_ci elems->prof = NULL; 157462306a36Sopenharmony_ci elems->sta_prof_len = 0; 157562306a36Sopenharmony_ci return; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci /* 157962306a36Sopenharmony_ci * Skip the capability information and the status code that are expected 158062306a36Sopenharmony_ci * as part of the station profile in association response frames. Note 158162306a36Sopenharmony_ci * the -1 is because the 'sta_info_len' is accounted to as part of the 158262306a36Sopenharmony_ci * per-STA profile, but not part of the 'u8 variable[]' portion. 158362306a36Sopenharmony_ci */ 158462306a36Sopenharmony_ci sub.start = prof->variable + prof->sta_info_len - 1 + 4; 158562306a36Sopenharmony_ci end = (const u8 *)prof + elems->sta_prof_len; 158662306a36Sopenharmony_ci sub.len = end - sub.start; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, 158962306a36Sopenharmony_ci sub.start, sub.len); 159062306a36Sopenharmony_ci _ieee802_11_parse_elems_full(&sub, elems, non_inherit); 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistruct ieee802_11_elems * 159462306a36Sopenharmony_ciieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) 159562306a36Sopenharmony_ci{ 159662306a36Sopenharmony_ci struct ieee802_11_elems *elems; 159762306a36Sopenharmony_ci const struct element *non_inherit = NULL; 159862306a36Sopenharmony_ci u8 *nontransmitted_profile; 159962306a36Sopenharmony_ci int nontransmitted_profile_len = 0; 160062306a36Sopenharmony_ci size_t scratch_len = 3 * params->len; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC); 160362306a36Sopenharmony_ci if (!elems) 160462306a36Sopenharmony_ci return NULL; 160562306a36Sopenharmony_ci elems->ie_start = params->start; 160662306a36Sopenharmony_ci elems->total_len = params->len; 160762306a36Sopenharmony_ci elems->scratch_len = scratch_len; 160862306a36Sopenharmony_ci elems->scratch_pos = elems->scratch; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci nontransmitted_profile = elems->scratch_pos; 161162306a36Sopenharmony_ci nontransmitted_profile_len = 161262306a36Sopenharmony_ci ieee802_11_find_bssid_profile(params->start, params->len, 161362306a36Sopenharmony_ci elems, params->bss, 161462306a36Sopenharmony_ci nontransmitted_profile); 161562306a36Sopenharmony_ci elems->scratch_pos += nontransmitted_profile_len; 161662306a36Sopenharmony_ci elems->scratch_len -= nontransmitted_profile_len; 161762306a36Sopenharmony_ci non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, 161862306a36Sopenharmony_ci nontransmitted_profile, 161962306a36Sopenharmony_ci nontransmitted_profile_len); 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci elems->crc = _ieee802_11_parse_elems_full(params, elems, non_inherit); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* Override with nontransmitted profile, if found */ 162462306a36Sopenharmony_ci if (nontransmitted_profile_len) { 162562306a36Sopenharmony_ci struct ieee80211_elems_parse_params sub = { 162662306a36Sopenharmony_ci .start = nontransmitted_profile, 162762306a36Sopenharmony_ci .len = nontransmitted_profile_len, 162862306a36Sopenharmony_ci .action = params->action, 162962306a36Sopenharmony_ci .link_id = params->link_id, 163062306a36Sopenharmony_ci }; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci _ieee802_11_parse_elems_full(&sub, elems, NULL); 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci ieee80211_mle_parse_link(elems, params); 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci if (elems->tim && !elems->parse_error) { 163862306a36Sopenharmony_ci const struct ieee80211_tim_ie *tim_ie = elems->tim; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci elems->dtim_period = tim_ie->dtim_period; 164162306a36Sopenharmony_ci elems->dtim_count = tim_ie->dtim_count; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci /* Override DTIM period and count if needed */ 164562306a36Sopenharmony_ci if (elems->bssid_index && 164662306a36Sopenharmony_ci elems->bssid_index_len >= 164762306a36Sopenharmony_ci offsetofend(struct ieee80211_bssid_index, dtim_period)) 164862306a36Sopenharmony_ci elems->dtim_period = elems->bssid_index->dtim_period; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (elems->bssid_index && 165162306a36Sopenharmony_ci elems->bssid_index_len >= 165262306a36Sopenharmony_ci offsetofend(struct ieee80211_bssid_index, dtim_count)) 165362306a36Sopenharmony_ci elems->dtim_count = elems->bssid_index->dtim_count; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci return elems; 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_civoid ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, 165962306a36Sopenharmony_ci struct ieee80211_tx_queue_params 166062306a36Sopenharmony_ci *qparam, int ac) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 166362306a36Sopenharmony_ci const struct ieee80211_reg_rule *rrule; 166462306a36Sopenharmony_ci const struct ieee80211_wmm_ac *wmm_ac; 166562306a36Sopenharmony_ci u16 center_freq = 0; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_AP && 166862306a36Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_STATION) 166962306a36Sopenharmony_ci return; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci rcu_read_lock(); 167262306a36Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); 167362306a36Sopenharmony_ci if (chanctx_conf) 167462306a36Sopenharmony_ci center_freq = chanctx_conf->def.chan->center_freq; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci if (!center_freq) { 167762306a36Sopenharmony_ci rcu_read_unlock(); 167862306a36Sopenharmony_ci return; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq)); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) { 168462306a36Sopenharmony_ci rcu_read_unlock(); 168562306a36Sopenharmony_ci return; 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) 168962306a36Sopenharmony_ci wmm_ac = &rrule->wmm_rule.ap[ac]; 169062306a36Sopenharmony_ci else 169162306a36Sopenharmony_ci wmm_ac = &rrule->wmm_rule.client[ac]; 169262306a36Sopenharmony_ci qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min); 169362306a36Sopenharmony_ci qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max); 169462306a36Sopenharmony_ci qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn); 169562306a36Sopenharmony_ci qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32); 169662306a36Sopenharmony_ci rcu_read_unlock(); 169762306a36Sopenharmony_ci} 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_civoid ieee80211_set_wmm_default(struct ieee80211_link_data *link, 170062306a36Sopenharmony_ci bool bss_notify, bool enable_qos) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = link->sdata; 170362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 170462306a36Sopenharmony_ci struct ieee80211_tx_queue_params qparam; 170562306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 170662306a36Sopenharmony_ci int ac; 170762306a36Sopenharmony_ci bool use_11b; 170862306a36Sopenharmony_ci bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */ 170962306a36Sopenharmony_ci int aCWmin, aCWmax; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci if (!local->ops->conf_tx) 171262306a36Sopenharmony_ci return; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 171562306a36Sopenharmony_ci return; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci memset(&qparam, 0, sizeof(qparam)); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci rcu_read_lock(); 172062306a36Sopenharmony_ci chanctx_conf = rcu_dereference(link->conf->chanctx_conf); 172162306a36Sopenharmony_ci use_11b = (chanctx_conf && 172262306a36Sopenharmony_ci chanctx_conf->def.chan->band == NL80211_BAND_2GHZ) && 172362306a36Sopenharmony_ci !link->operating_11g_mode; 172462306a36Sopenharmony_ci rcu_read_unlock(); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci /* Set defaults according to 802.11-2007 Table 7-37 */ 172962306a36Sopenharmony_ci aCWmax = 1023; 173062306a36Sopenharmony_ci if (use_11b) 173162306a36Sopenharmony_ci aCWmin = 31; 173262306a36Sopenharmony_ci else 173362306a36Sopenharmony_ci aCWmin = 15; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci /* Confiure old 802.11b/g medium access rules. */ 173662306a36Sopenharmony_ci qparam.cw_max = aCWmax; 173762306a36Sopenharmony_ci qparam.cw_min = aCWmin; 173862306a36Sopenharmony_ci qparam.txop = 0; 173962306a36Sopenharmony_ci qparam.aifs = 2; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 174262306a36Sopenharmony_ci /* Update if QoS is enabled. */ 174362306a36Sopenharmony_ci if (enable_qos) { 174462306a36Sopenharmony_ci switch (ac) { 174562306a36Sopenharmony_ci case IEEE80211_AC_BK: 174662306a36Sopenharmony_ci qparam.cw_max = aCWmax; 174762306a36Sopenharmony_ci qparam.cw_min = aCWmin; 174862306a36Sopenharmony_ci qparam.txop = 0; 174962306a36Sopenharmony_ci if (is_ocb) 175062306a36Sopenharmony_ci qparam.aifs = 9; 175162306a36Sopenharmony_ci else 175262306a36Sopenharmony_ci qparam.aifs = 7; 175362306a36Sopenharmony_ci break; 175462306a36Sopenharmony_ci /* never happens but let's not leave undefined */ 175562306a36Sopenharmony_ci default: 175662306a36Sopenharmony_ci case IEEE80211_AC_BE: 175762306a36Sopenharmony_ci qparam.cw_max = aCWmax; 175862306a36Sopenharmony_ci qparam.cw_min = aCWmin; 175962306a36Sopenharmony_ci qparam.txop = 0; 176062306a36Sopenharmony_ci if (is_ocb) 176162306a36Sopenharmony_ci qparam.aifs = 6; 176262306a36Sopenharmony_ci else 176362306a36Sopenharmony_ci qparam.aifs = 3; 176462306a36Sopenharmony_ci break; 176562306a36Sopenharmony_ci case IEEE80211_AC_VI: 176662306a36Sopenharmony_ci qparam.cw_max = aCWmin; 176762306a36Sopenharmony_ci qparam.cw_min = (aCWmin + 1) / 2 - 1; 176862306a36Sopenharmony_ci if (is_ocb) 176962306a36Sopenharmony_ci qparam.txop = 0; 177062306a36Sopenharmony_ci else if (use_11b) 177162306a36Sopenharmony_ci qparam.txop = 6016/32; 177262306a36Sopenharmony_ci else 177362306a36Sopenharmony_ci qparam.txop = 3008/32; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (is_ocb) 177662306a36Sopenharmony_ci qparam.aifs = 3; 177762306a36Sopenharmony_ci else 177862306a36Sopenharmony_ci qparam.aifs = 2; 177962306a36Sopenharmony_ci break; 178062306a36Sopenharmony_ci case IEEE80211_AC_VO: 178162306a36Sopenharmony_ci qparam.cw_max = (aCWmin + 1) / 2 - 1; 178262306a36Sopenharmony_ci qparam.cw_min = (aCWmin + 1) / 4 - 1; 178362306a36Sopenharmony_ci if (is_ocb) 178462306a36Sopenharmony_ci qparam.txop = 0; 178562306a36Sopenharmony_ci else if (use_11b) 178662306a36Sopenharmony_ci qparam.txop = 3264/32; 178762306a36Sopenharmony_ci else 178862306a36Sopenharmony_ci qparam.txop = 1504/32; 178962306a36Sopenharmony_ci qparam.aifs = 2; 179062306a36Sopenharmony_ci break; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci ieee80211_regulatory_limit_wmm_params(sdata, &qparam, ac); 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci qparam.uapsd = false; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci link->tx_conf[ac] = qparam; 179862306a36Sopenharmony_ci drv_conf_tx(local, link, ac, &qparam); 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_MONITOR && 180262306a36Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 180362306a36Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_NAN) { 180462306a36Sopenharmony_ci link->conf->qos = enable_qos; 180562306a36Sopenharmony_ci if (bss_notify) 180662306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, 180762306a36Sopenharmony_ci BSS_CHANGED_QOS); 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_civoid ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 181262306a36Sopenharmony_ci u16 transaction, u16 auth_alg, u16 status, 181362306a36Sopenharmony_ci const u8 *extra, size_t extra_len, const u8 *da, 181462306a36Sopenharmony_ci const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, 181562306a36Sopenharmony_ci u32 tx_flags) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 181862306a36Sopenharmony_ci struct sk_buff *skb; 181962306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 182062306a36Sopenharmony_ci bool multi_link = ieee80211_vif_is_mld(&sdata->vif); 182162306a36Sopenharmony_ci struct { 182262306a36Sopenharmony_ci u8 id; 182362306a36Sopenharmony_ci u8 len; 182462306a36Sopenharmony_ci u8 ext_id; 182562306a36Sopenharmony_ci struct ieee80211_multi_link_elem ml; 182662306a36Sopenharmony_ci struct ieee80211_mle_basic_common_info basic; 182762306a36Sopenharmony_ci } __packed mle = { 182862306a36Sopenharmony_ci .id = WLAN_EID_EXTENSION, 182962306a36Sopenharmony_ci .len = sizeof(mle) - 2, 183062306a36Sopenharmony_ci .ext_id = WLAN_EID_EXT_EHT_MULTI_LINK, 183162306a36Sopenharmony_ci .ml.control = cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC), 183262306a36Sopenharmony_ci .basic.len = sizeof(mle.basic), 183362306a36Sopenharmony_ci }; 183462306a36Sopenharmony_ci int err; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci memcpy(mle.basic.mld_mac_addr, sdata->vif.addr, ETH_ALEN); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci /* 24 + 6 = header + auth_algo + auth_transaction + status_code */ 183962306a36Sopenharmony_ci skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN + 184062306a36Sopenharmony_ci 24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN + 184162306a36Sopenharmony_ci multi_link * sizeof(mle)); 184262306a36Sopenharmony_ci if (!skb) 184362306a36Sopenharmony_ci return; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci mgmt = skb_put_zero(skb, 24 + 6); 184862306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 184962306a36Sopenharmony_ci IEEE80211_STYPE_AUTH); 185062306a36Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 185162306a36Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 185262306a36Sopenharmony_ci memcpy(mgmt->bssid, bssid, ETH_ALEN); 185362306a36Sopenharmony_ci mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); 185462306a36Sopenharmony_ci mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); 185562306a36Sopenharmony_ci mgmt->u.auth.status_code = cpu_to_le16(status); 185662306a36Sopenharmony_ci if (extra) 185762306a36Sopenharmony_ci skb_put_data(skb, extra, extra_len); 185862306a36Sopenharmony_ci if (multi_link) 185962306a36Sopenharmony_ci skb_put_data(skb, &mle, sizeof(mle)); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { 186262306a36Sopenharmony_ci mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 186362306a36Sopenharmony_ci err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); 186462306a36Sopenharmony_ci if (WARN_ON(err)) { 186562306a36Sopenharmony_ci kfree_skb(skb); 186662306a36Sopenharmony_ci return; 186762306a36Sopenharmony_ci } 186862306a36Sopenharmony_ci } 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | 187162306a36Sopenharmony_ci tx_flags; 187262306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_civoid ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, 187662306a36Sopenharmony_ci const u8 *da, const u8 *bssid, 187762306a36Sopenharmony_ci u16 stype, u16 reason, 187862306a36Sopenharmony_ci bool send_frame, u8 *frame_buf) 187962306a36Sopenharmony_ci{ 188062306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 188162306a36Sopenharmony_ci struct sk_buff *skb; 188262306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *)frame_buf; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci /* build frame */ 188562306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); 188662306a36Sopenharmony_ci mgmt->duration = 0; /* initialize only */ 188762306a36Sopenharmony_ci mgmt->seq_ctrl = 0; /* initialize only */ 188862306a36Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 188962306a36Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 189062306a36Sopenharmony_ci memcpy(mgmt->bssid, bssid, ETH_ALEN); 189162306a36Sopenharmony_ci /* u.deauth.reason_code == u.disassoc.reason_code */ 189262306a36Sopenharmony_ci mgmt->u.deauth.reason_code = cpu_to_le16(reason); 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci if (send_frame) { 189562306a36Sopenharmony_ci skb = dev_alloc_skb(local->hw.extra_tx_headroom + 189662306a36Sopenharmony_ci IEEE80211_DEAUTH_FRAME_LEN); 189762306a36Sopenharmony_ci if (!skb) 189862306a36Sopenharmony_ci return; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci /* copy in frame */ 190362306a36Sopenharmony_ci skb_put_data(skb, mgmt, IEEE80211_DEAUTH_FRAME_LEN); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION || 190662306a36Sopenharmony_ci !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) 190762306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= 190862306a36Sopenharmony_ci IEEE80211_TX_INTFL_DONT_ENCRYPT; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 191162306a36Sopenharmony_ci } 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ciu8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci if ((end - pos) < 5) 191762306a36Sopenharmony_ci return pos; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 192062306a36Sopenharmony_ci *pos++ = 1 + sizeof(cap); 192162306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_HE_6GHZ_CAPA; 192262306a36Sopenharmony_ci memcpy(pos, &cap, sizeof(cap)); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci return pos + 2; 192562306a36Sopenharmony_ci} 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_cistatic int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, 192862306a36Sopenharmony_ci u8 *buffer, size_t buffer_len, 192962306a36Sopenharmony_ci const u8 *ie, size_t ie_len, 193062306a36Sopenharmony_ci enum nl80211_band band, 193162306a36Sopenharmony_ci u32 rate_mask, 193262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, 193362306a36Sopenharmony_ci size_t *offset, u32 flags) 193462306a36Sopenharmony_ci{ 193562306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 193662306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 193762306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 193862306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap; 193962306a36Sopenharmony_ci u8 *pos = buffer, *end = buffer + buffer_len; 194062306a36Sopenharmony_ci size_t noffset; 194162306a36Sopenharmony_ci int supp_rates_len, i; 194262306a36Sopenharmony_ci u8 rates[32]; 194362306a36Sopenharmony_ci int num_rates; 194462306a36Sopenharmony_ci int ext_rates_len; 194562306a36Sopenharmony_ci int shift; 194662306a36Sopenharmony_ci u32 rate_flags; 194762306a36Sopenharmony_ci bool have_80mhz = false; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci *offset = 0; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 195262306a36Sopenharmony_ci if (WARN_ON_ONCE(!sband)) 195362306a36Sopenharmony_ci return 0; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(chandef); 195662306a36Sopenharmony_ci shift = ieee80211_chandef_get_shift(chandef); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci /* For direct scan add S1G IE and consider its override bits */ 195962306a36Sopenharmony_ci if (band == NL80211_BAND_S1GHZ) { 196062306a36Sopenharmony_ci if (end - pos < 2 + sizeof(struct ieee80211_s1g_cap)) 196162306a36Sopenharmony_ci goto out_err; 196262306a36Sopenharmony_ci pos = ieee80211_ie_build_s1g_cap(pos, &sband->s1g_cap); 196362306a36Sopenharmony_ci goto done; 196462306a36Sopenharmony_ci } 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci num_rates = 0; 196762306a36Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 196862306a36Sopenharmony_ci if ((BIT(i) & rate_mask) == 0) 196962306a36Sopenharmony_ci continue; /* skip rate */ 197062306a36Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 197162306a36Sopenharmony_ci continue; 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci rates[num_rates++] = 197462306a36Sopenharmony_ci (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, 197562306a36Sopenharmony_ci (1 << shift) * 5); 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci supp_rates_len = min_t(int, num_rates, 8); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci if (end - pos < 2 + supp_rates_len) 198162306a36Sopenharmony_ci goto out_err; 198262306a36Sopenharmony_ci *pos++ = WLAN_EID_SUPP_RATES; 198362306a36Sopenharmony_ci *pos++ = supp_rates_len; 198462306a36Sopenharmony_ci memcpy(pos, rates, supp_rates_len); 198562306a36Sopenharmony_ci pos += supp_rates_len; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci /* insert "request information" if in custom IEs */ 198862306a36Sopenharmony_ci if (ie && ie_len) { 198962306a36Sopenharmony_ci static const u8 before_extrates[] = { 199062306a36Sopenharmony_ci WLAN_EID_SSID, 199162306a36Sopenharmony_ci WLAN_EID_SUPP_RATES, 199262306a36Sopenharmony_ci WLAN_EID_REQUEST, 199362306a36Sopenharmony_ci }; 199462306a36Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 199562306a36Sopenharmony_ci before_extrates, 199662306a36Sopenharmony_ci ARRAY_SIZE(before_extrates), 199762306a36Sopenharmony_ci *offset); 199862306a36Sopenharmony_ci if (end - pos < noffset - *offset) 199962306a36Sopenharmony_ci goto out_err; 200062306a36Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 200162306a36Sopenharmony_ci pos += noffset - *offset; 200262306a36Sopenharmony_ci *offset = noffset; 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci ext_rates_len = num_rates - supp_rates_len; 200662306a36Sopenharmony_ci if (ext_rates_len > 0) { 200762306a36Sopenharmony_ci if (end - pos < 2 + ext_rates_len) 200862306a36Sopenharmony_ci goto out_err; 200962306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_SUPP_RATES; 201062306a36Sopenharmony_ci *pos++ = ext_rates_len; 201162306a36Sopenharmony_ci memcpy(pos, rates + supp_rates_len, ext_rates_len); 201262306a36Sopenharmony_ci pos += ext_rates_len; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci if (chandef->chan && sband->band == NL80211_BAND_2GHZ) { 201662306a36Sopenharmony_ci if (end - pos < 3) 201762306a36Sopenharmony_ci goto out_err; 201862306a36Sopenharmony_ci *pos++ = WLAN_EID_DS_PARAMS; 201962306a36Sopenharmony_ci *pos++ = 1; 202062306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel( 202162306a36Sopenharmony_ci chandef->chan->center_freq); 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT) 202562306a36Sopenharmony_ci goto done; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci /* insert custom IEs that go before HT */ 202862306a36Sopenharmony_ci if (ie && ie_len) { 202962306a36Sopenharmony_ci static const u8 before_ht[] = { 203062306a36Sopenharmony_ci /* 203162306a36Sopenharmony_ci * no need to list the ones split off already 203262306a36Sopenharmony_ci * (or generated here) 203362306a36Sopenharmony_ci */ 203462306a36Sopenharmony_ci WLAN_EID_DS_PARAMS, 203562306a36Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 203662306a36Sopenharmony_ci }; 203762306a36Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 203862306a36Sopenharmony_ci before_ht, ARRAY_SIZE(before_ht), 203962306a36Sopenharmony_ci *offset); 204062306a36Sopenharmony_ci if (end - pos < noffset - *offset) 204162306a36Sopenharmony_ci goto out_err; 204262306a36Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 204362306a36Sopenharmony_ci pos += noffset - *offset; 204462306a36Sopenharmony_ci *offset = noffset; 204562306a36Sopenharmony_ci } 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (sband->ht_cap.ht_supported) { 204862306a36Sopenharmony_ci if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) 204962306a36Sopenharmony_ci goto out_err; 205062306a36Sopenharmony_ci pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, 205162306a36Sopenharmony_ci sband->ht_cap.cap); 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci /* insert custom IEs that go before VHT */ 205562306a36Sopenharmony_ci if (ie && ie_len) { 205662306a36Sopenharmony_ci static const u8 before_vht[] = { 205762306a36Sopenharmony_ci /* 205862306a36Sopenharmony_ci * no need to list the ones split off already 205962306a36Sopenharmony_ci * (or generated here) 206062306a36Sopenharmony_ci */ 206162306a36Sopenharmony_ci WLAN_EID_BSS_COEX_2040, 206262306a36Sopenharmony_ci WLAN_EID_EXT_CAPABILITY, 206362306a36Sopenharmony_ci WLAN_EID_SSID_LIST, 206462306a36Sopenharmony_ci WLAN_EID_CHANNEL_USAGE, 206562306a36Sopenharmony_ci WLAN_EID_INTERWORKING, 206662306a36Sopenharmony_ci WLAN_EID_MESH_ID, 206762306a36Sopenharmony_ci /* 60 GHz (Multi-band, DMG, MMS) can't happen */ 206862306a36Sopenharmony_ci }; 206962306a36Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 207062306a36Sopenharmony_ci before_vht, ARRAY_SIZE(before_vht), 207162306a36Sopenharmony_ci *offset); 207262306a36Sopenharmony_ci if (end - pos < noffset - *offset) 207362306a36Sopenharmony_ci goto out_err; 207462306a36Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 207562306a36Sopenharmony_ci pos += noffset - *offset; 207662306a36Sopenharmony_ci *offset = noffset; 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci /* Check if any channel in this sband supports at least 80 MHz */ 208062306a36Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 208162306a36Sopenharmony_ci if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 208262306a36Sopenharmony_ci IEEE80211_CHAN_NO_80MHZ)) 208362306a36Sopenharmony_ci continue; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci have_80mhz = true; 208662306a36Sopenharmony_ci break; 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci if (sband->vht_cap.vht_supported && have_80mhz) { 209062306a36Sopenharmony_ci if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) 209162306a36Sopenharmony_ci goto out_err; 209262306a36Sopenharmony_ci pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, 209362306a36Sopenharmony_ci sband->vht_cap.cap); 209462306a36Sopenharmony_ci } 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci /* insert custom IEs that go before HE */ 209762306a36Sopenharmony_ci if (ie && ie_len) { 209862306a36Sopenharmony_ci static const u8 before_he[] = { 209962306a36Sopenharmony_ci /* 210062306a36Sopenharmony_ci * no need to list the ones split off before VHT 210162306a36Sopenharmony_ci * or generated here 210262306a36Sopenharmony_ci */ 210362306a36Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS, 210462306a36Sopenharmony_ci WLAN_EID_AP_CSN, 210562306a36Sopenharmony_ci /* TODO: add 11ah/11aj/11ak elements */ 210662306a36Sopenharmony_ci }; 210762306a36Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 210862306a36Sopenharmony_ci before_he, ARRAY_SIZE(before_he), 210962306a36Sopenharmony_ci *offset); 211062306a36Sopenharmony_ci if (end - pos < noffset - *offset) 211162306a36Sopenharmony_ci goto out_err; 211262306a36Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 211362306a36Sopenharmony_ci pos += noffset - *offset; 211462306a36Sopenharmony_ci *offset = noffset; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap_vif(sband, &sdata->vif); 211862306a36Sopenharmony_ci if (he_cap && 211962306a36Sopenharmony_ci cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), 212062306a36Sopenharmony_ci IEEE80211_CHAN_NO_HE)) { 212162306a36Sopenharmony_ci pos = ieee80211_ie_build_he_cap(0, pos, he_cap, end); 212262306a36Sopenharmony_ci if (!pos) 212362306a36Sopenharmony_ci goto out_err; 212462306a36Sopenharmony_ci } 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, &sdata->vif); 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci if (eht_cap && 212962306a36Sopenharmony_ci cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), 213062306a36Sopenharmony_ci IEEE80211_CHAN_NO_HE | 213162306a36Sopenharmony_ci IEEE80211_CHAN_NO_EHT)) { 213262306a36Sopenharmony_ci pos = ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, end, 213362306a36Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_AP); 213462306a36Sopenharmony_ci if (!pos) 213562306a36Sopenharmony_ci goto out_err; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci if (cfg80211_any_usable_channels(local->hw.wiphy, 213962306a36Sopenharmony_ci BIT(NL80211_BAND_6GHZ), 214062306a36Sopenharmony_ci IEEE80211_CHAN_NO_HE)) { 214162306a36Sopenharmony_ci struct ieee80211_supported_band *sband6; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci sband6 = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 214462306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap_vif(sband6, &sdata->vif); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (he_cap) { 214762306a36Sopenharmony_ci enum nl80211_iftype iftype = 214862306a36Sopenharmony_ci ieee80211_vif_type_p2p(&sdata->vif); 214962306a36Sopenharmony_ci __le16 cap = ieee80211_get_he_6ghz_capa(sband6, iftype); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci pos = ieee80211_write_he_6ghz_cap(pos, cap, end); 215262306a36Sopenharmony_ci } 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci /* 215662306a36Sopenharmony_ci * If adding more here, adjust code in main.c 215762306a36Sopenharmony_ci * that calculates local->scan_ies_len. 215862306a36Sopenharmony_ci */ 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci return pos - buffer; 216162306a36Sopenharmony_ci out_err: 216262306a36Sopenharmony_ci WARN_ONCE(1, "not enough space for preq IEs\n"); 216362306a36Sopenharmony_ci done: 216462306a36Sopenharmony_ci return pos - buffer; 216562306a36Sopenharmony_ci} 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ciint ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer, 216862306a36Sopenharmony_ci size_t buffer_len, 216962306a36Sopenharmony_ci struct ieee80211_scan_ies *ie_desc, 217062306a36Sopenharmony_ci const u8 *ie, size_t ie_len, 217162306a36Sopenharmony_ci u8 bands_used, u32 *rate_masks, 217262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef, 217362306a36Sopenharmony_ci u32 flags) 217462306a36Sopenharmony_ci{ 217562306a36Sopenharmony_ci size_t pos = 0, old_pos = 0, custom_ie_offset = 0; 217662306a36Sopenharmony_ci int i; 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci memset(ie_desc, 0, sizeof(*ie_desc)); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci for (i = 0; i < NUM_NL80211_BANDS; i++) { 218162306a36Sopenharmony_ci if (bands_used & BIT(i)) { 218262306a36Sopenharmony_ci pos += ieee80211_build_preq_ies_band(sdata, 218362306a36Sopenharmony_ci buffer + pos, 218462306a36Sopenharmony_ci buffer_len - pos, 218562306a36Sopenharmony_ci ie, ie_len, i, 218662306a36Sopenharmony_ci rate_masks[i], 218762306a36Sopenharmony_ci chandef, 218862306a36Sopenharmony_ci &custom_ie_offset, 218962306a36Sopenharmony_ci flags); 219062306a36Sopenharmony_ci ie_desc->ies[i] = buffer + old_pos; 219162306a36Sopenharmony_ci ie_desc->len[i] = pos - old_pos; 219262306a36Sopenharmony_ci old_pos = pos; 219362306a36Sopenharmony_ci } 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci /* add any remaining custom IEs */ 219762306a36Sopenharmony_ci if (ie && ie_len) { 219862306a36Sopenharmony_ci if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset, 219962306a36Sopenharmony_ci "not enough space for preq custom IEs\n")) 220062306a36Sopenharmony_ci return pos; 220162306a36Sopenharmony_ci memcpy(buffer + pos, ie + custom_ie_offset, 220262306a36Sopenharmony_ci ie_len - custom_ie_offset); 220362306a36Sopenharmony_ci ie_desc->common_ies = buffer + pos; 220462306a36Sopenharmony_ci ie_desc->common_ie_len = ie_len - custom_ie_offset; 220562306a36Sopenharmony_ci pos += ie_len - custom_ie_offset; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci return pos; 220962306a36Sopenharmony_ci}; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_cistruct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, 221262306a36Sopenharmony_ci const u8 *src, const u8 *dst, 221362306a36Sopenharmony_ci u32 ratemask, 221462306a36Sopenharmony_ci struct ieee80211_channel *chan, 221562306a36Sopenharmony_ci const u8 *ssid, size_t ssid_len, 221662306a36Sopenharmony_ci const u8 *ie, size_t ie_len, 221762306a36Sopenharmony_ci u32 flags) 221862306a36Sopenharmony_ci{ 221962306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 222062306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 222162306a36Sopenharmony_ci struct sk_buff *skb; 222262306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 222362306a36Sopenharmony_ci int ies_len; 222462306a36Sopenharmony_ci u32 rate_masks[NUM_NL80211_BANDS] = {}; 222562306a36Sopenharmony_ci struct ieee80211_scan_ies dummy_ie_desc; 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci /* 222862306a36Sopenharmony_ci * Do not send DS Channel parameter for directed probe requests 222962306a36Sopenharmony_ci * in order to maximize the chance that we get a response. Some 223062306a36Sopenharmony_ci * badly-behaved APs don't respond when this parameter is included. 223162306a36Sopenharmony_ci */ 223262306a36Sopenharmony_ci chandef.width = sdata->vif.bss_conf.chandef.width; 223362306a36Sopenharmony_ci if (flags & IEEE80211_PROBE_FLAG_DIRECTED) 223462306a36Sopenharmony_ci chandef.chan = NULL; 223562306a36Sopenharmony_ci else 223662306a36Sopenharmony_ci chandef.chan = chan; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len, 223962306a36Sopenharmony_ci local->scan_ies_len + ie_len); 224062306a36Sopenharmony_ci if (!skb) 224162306a36Sopenharmony_ci return NULL; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci rate_masks[chan->band] = ratemask; 224462306a36Sopenharmony_ci ies_len = ieee80211_build_preq_ies(sdata, skb_tail_pointer(skb), 224562306a36Sopenharmony_ci skb_tailroom(skb), &dummy_ie_desc, 224662306a36Sopenharmony_ci ie, ie_len, BIT(chan->band), 224762306a36Sopenharmony_ci rate_masks, &chandef, flags); 224862306a36Sopenharmony_ci skb_put(skb, ies_len); 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci if (dst) { 225162306a36Sopenharmony_ci mgmt = (struct ieee80211_mgmt *) skb->data; 225262306a36Sopenharmony_ci memcpy(mgmt->da, dst, ETH_ALEN); 225362306a36Sopenharmony_ci memcpy(mgmt->bssid, dst, ETH_ALEN); 225462306a36Sopenharmony_ci } 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci return skb; 225962306a36Sopenharmony_ci} 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ciu32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, 226262306a36Sopenharmony_ci struct ieee802_11_elems *elems, 226362306a36Sopenharmony_ci enum nl80211_band band, u32 *basic_rates) 226462306a36Sopenharmony_ci{ 226562306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 226662306a36Sopenharmony_ci size_t num_rates; 226762306a36Sopenharmony_ci u32 supp_rates, rate_flags; 226862306a36Sopenharmony_ci int i, j, shift; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci sband = sdata->local->hw.wiphy->bands[band]; 227162306a36Sopenharmony_ci if (WARN_ON(!sband)) 227262306a36Sopenharmony_ci return 1; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 227562306a36Sopenharmony_ci shift = ieee80211_vif_get_shift(&sdata->vif); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci num_rates = sband->n_bitrates; 227862306a36Sopenharmony_ci supp_rates = 0; 227962306a36Sopenharmony_ci for (i = 0; i < elems->supp_rates_len + 228062306a36Sopenharmony_ci elems->ext_supp_rates_len; i++) { 228162306a36Sopenharmony_ci u8 rate = 0; 228262306a36Sopenharmony_ci int own_rate; 228362306a36Sopenharmony_ci bool is_basic; 228462306a36Sopenharmony_ci if (i < elems->supp_rates_len) 228562306a36Sopenharmony_ci rate = elems->supp_rates[i]; 228662306a36Sopenharmony_ci else if (elems->ext_supp_rates) 228762306a36Sopenharmony_ci rate = elems->ext_supp_rates 228862306a36Sopenharmony_ci [i - elems->supp_rates_len]; 228962306a36Sopenharmony_ci own_rate = 5 * (rate & 0x7f); 229062306a36Sopenharmony_ci is_basic = !!(rate & 0x80); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) 229362306a36Sopenharmony_ci continue; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci for (j = 0; j < num_rates; j++) { 229662306a36Sopenharmony_ci int brate; 229762306a36Sopenharmony_ci if ((rate_flags & sband->bitrates[j].flags) 229862306a36Sopenharmony_ci != rate_flags) 229962306a36Sopenharmony_ci continue; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, 230262306a36Sopenharmony_ci 1 << shift); 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci if (brate == own_rate) { 230562306a36Sopenharmony_ci supp_rates |= BIT(j); 230662306a36Sopenharmony_ci if (basic_rates && is_basic) 230762306a36Sopenharmony_ci *basic_rates |= BIT(j); 230862306a36Sopenharmony_ci } 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci return supp_rates; 231262306a36Sopenharmony_ci} 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_civoid ieee80211_stop_device(struct ieee80211_local *local) 231562306a36Sopenharmony_ci{ 231662306a36Sopenharmony_ci ieee80211_led_radio(local, false); 231762306a36Sopenharmony_ci ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci cancel_work_sync(&local->reconfig_filter); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci flush_workqueue(local->workqueue); 232262306a36Sopenharmony_ci drv_stop(local); 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cistatic void ieee80211_flush_completed_scan(struct ieee80211_local *local, 232662306a36Sopenharmony_ci bool aborted) 232762306a36Sopenharmony_ci{ 232862306a36Sopenharmony_ci /* It's possible that we don't handle the scan completion in 232962306a36Sopenharmony_ci * time during suspend, so if it's still marked as completed 233062306a36Sopenharmony_ci * here, queue the work and flush it to clean things up. 233162306a36Sopenharmony_ci * Instead of calling the worker function directly here, we 233262306a36Sopenharmony_ci * really queue it to avoid potential races with other flows 233362306a36Sopenharmony_ci * scheduling the same work. 233462306a36Sopenharmony_ci */ 233562306a36Sopenharmony_ci if (test_bit(SCAN_COMPLETED, &local->scanning)) { 233662306a36Sopenharmony_ci /* If coming from reconfiguration failure, abort the scan so 233762306a36Sopenharmony_ci * we don't attempt to continue a partial HW scan - which is 233862306a36Sopenharmony_ci * possible otherwise if (e.g.) the 2.4 GHz portion was the 233962306a36Sopenharmony_ci * completed scan, and a 5 GHz portion is still pending. 234062306a36Sopenharmony_ci */ 234162306a36Sopenharmony_ci if (aborted) 234262306a36Sopenharmony_ci set_bit(SCAN_ABORTED, &local->scanning); 234362306a36Sopenharmony_ci wiphy_delayed_work_queue(local->hw.wiphy, &local->scan_work, 0); 234462306a36Sopenharmony_ci wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work); 234562306a36Sopenharmony_ci } 234662306a36Sopenharmony_ci} 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_cistatic void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) 234962306a36Sopenharmony_ci{ 235062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 235162306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci /* 235462306a36Sopenharmony_ci * We get here if during resume the device can't be restarted properly. 235562306a36Sopenharmony_ci * We might also get here if this happens during HW reset, which is a 235662306a36Sopenharmony_ci * slightly different situation and we need to drop all connections in 235762306a36Sopenharmony_ci * the latter case. 235862306a36Sopenharmony_ci * 235962306a36Sopenharmony_ci * Ask cfg80211 to turn off all interfaces, this will result in more 236062306a36Sopenharmony_ci * warnings but at least we'll then get into a clean stopped state. 236162306a36Sopenharmony_ci */ 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci local->resuming = false; 236462306a36Sopenharmony_ci local->suspended = false; 236562306a36Sopenharmony_ci local->in_reconfig = false; 236662306a36Sopenharmony_ci local->reconfig_failure = true; 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci ieee80211_flush_completed_scan(local, true); 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci /* scheduled scan clearly can't be running any more, but tell 237162306a36Sopenharmony_ci * cfg80211 and clear local state 237262306a36Sopenharmony_ci */ 237362306a36Sopenharmony_ci ieee80211_sched_scan_end(local); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) 237662306a36Sopenharmony_ci sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* Mark channel contexts as not being in the driver any more to avoid 237962306a36Sopenharmony_ci * removing them from the driver during the shutdown process... 238062306a36Sopenharmony_ci */ 238162306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 238262306a36Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) 238362306a36Sopenharmony_ci ctx->driver_present = false; 238462306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 238562306a36Sopenharmony_ci} 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_cistatic void ieee80211_assign_chanctx(struct ieee80211_local *local, 238862306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 238962306a36Sopenharmony_ci struct ieee80211_link_data *link) 239062306a36Sopenharmony_ci{ 239162306a36Sopenharmony_ci struct ieee80211_chanctx_conf *conf; 239262306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci if (!local->use_chanctx) 239562306a36Sopenharmony_ci return; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 239862306a36Sopenharmony_ci conf = rcu_dereference_protected(link->conf->chanctx_conf, 239962306a36Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 240062306a36Sopenharmony_ci if (conf) { 240162306a36Sopenharmony_ci ctx = container_of(conf, struct ieee80211_chanctx, conf); 240262306a36Sopenharmony_ci drv_assign_vif_chanctx(local, sdata, link->conf, ctx); 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 240562306a36Sopenharmony_ci} 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_cistatic void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) 240862306a36Sopenharmony_ci{ 240962306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 241062306a36Sopenharmony_ci struct sta_info *sta; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci /* add STAs back */ 241362306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 241462306a36Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 241562306a36Sopenharmony_ci enum ieee80211_sta_state state; 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_ci if (!sta->uploaded || sta->sdata != sdata) 241862306a36Sopenharmony_ci continue; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci for (state = IEEE80211_STA_NOTEXIST; 242162306a36Sopenharmony_ci state < sta->sta_state; state++) 242262306a36Sopenharmony_ci WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 242362306a36Sopenharmony_ci state + 1)); 242462306a36Sopenharmony_ci } 242562306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 242662306a36Sopenharmony_ci} 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_cistatic int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) 242962306a36Sopenharmony_ci{ 243062306a36Sopenharmony_ci struct cfg80211_nan_func *func, **funcs; 243162306a36Sopenharmony_ci int res, id, i = 0; 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci res = drv_start_nan(sdata->local, sdata, 243462306a36Sopenharmony_ci &sdata->u.nan.conf); 243562306a36Sopenharmony_ci if (WARN_ON(res)) 243662306a36Sopenharmony_ci return res; 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci funcs = kcalloc(sdata->local->hw.max_nan_de_entries + 1, 243962306a36Sopenharmony_ci sizeof(*funcs), 244062306a36Sopenharmony_ci GFP_KERNEL); 244162306a36Sopenharmony_ci if (!funcs) 244262306a36Sopenharmony_ci return -ENOMEM; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci /* Add all the functions: 244562306a36Sopenharmony_ci * This is a little bit ugly. We need to call a potentially sleeping 244662306a36Sopenharmony_ci * callback for each NAN function, so we can't hold the spinlock. 244762306a36Sopenharmony_ci */ 244862306a36Sopenharmony_ci spin_lock_bh(&sdata->u.nan.func_lock); 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) 245162306a36Sopenharmony_ci funcs[i++] = func; 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci spin_unlock_bh(&sdata->u.nan.func_lock); 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci for (i = 0; funcs[i]; i++) { 245662306a36Sopenharmony_ci res = drv_add_nan_func(sdata->local, sdata, funcs[i]); 245762306a36Sopenharmony_ci if (WARN_ON(res)) 245862306a36Sopenharmony_ci ieee80211_nan_func_terminated(&sdata->vif, 245962306a36Sopenharmony_ci funcs[i]->instance_id, 246062306a36Sopenharmony_ci NL80211_NAN_FUNC_TERM_REASON_ERROR, 246162306a36Sopenharmony_ci GFP_KERNEL); 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci kfree(funcs); 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci return 0; 246762306a36Sopenharmony_ci} 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_cistatic void ieee80211_reconfig_ap_links(struct ieee80211_local *local, 247062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 247162306a36Sopenharmony_ci u64 changed) 247262306a36Sopenharmony_ci{ 247362306a36Sopenharmony_ci int link_id; 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) { 247662306a36Sopenharmony_ci struct ieee80211_link_data *link; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci if (!(sdata->vif.active_links & BIT(link_id))) 247962306a36Sopenharmony_ci continue; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 248262306a36Sopenharmony_ci if (!link) 248362306a36Sopenharmony_ci continue; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci if (rcu_access_pointer(link->u.ap.beacon)) 248662306a36Sopenharmony_ci drv_start_ap(local, sdata, link->conf); 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci if (!link->conf->enable_beacon) 248962306a36Sopenharmony_ci continue; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci changed |= BSS_CHANGED_BEACON | 249262306a36Sopenharmony_ci BSS_CHANGED_BEACON_ENABLED; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, changed); 249562306a36Sopenharmony_ci } 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ciint ieee80211_reconfig(struct ieee80211_local *local) 249962306a36Sopenharmony_ci{ 250062306a36Sopenharmony_ci struct ieee80211_hw *hw = &local->hw; 250162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 250262306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 250362306a36Sopenharmony_ci struct sta_info *sta; 250462306a36Sopenharmony_ci int res, i; 250562306a36Sopenharmony_ci bool reconfig_due_to_wowlan = false; 250662306a36Sopenharmony_ci struct ieee80211_sub_if_data *sched_scan_sdata; 250762306a36Sopenharmony_ci struct cfg80211_sched_scan_request *sched_scan_req; 250862306a36Sopenharmony_ci bool sched_scan_stopped = false; 250962306a36Sopenharmony_ci bool suspended = local->suspended; 251062306a36Sopenharmony_ci bool in_reconfig = false; 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci /* nothing to do if HW shouldn't run */ 251362306a36Sopenharmony_ci if (!local->open_count) 251462306a36Sopenharmony_ci goto wake_up; 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci#ifdef CONFIG_PM 251762306a36Sopenharmony_ci if (suspended) 251862306a36Sopenharmony_ci local->resuming = true; 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci if (local->wowlan) { 252162306a36Sopenharmony_ci /* 252262306a36Sopenharmony_ci * In the wowlan case, both mac80211 and the device 252362306a36Sopenharmony_ci * are functional when the resume op is called, so 252462306a36Sopenharmony_ci * clear local->suspended so the device could operate 252562306a36Sopenharmony_ci * normally (e.g. pass rx frames). 252662306a36Sopenharmony_ci */ 252762306a36Sopenharmony_ci local->suspended = false; 252862306a36Sopenharmony_ci res = drv_resume(local); 252962306a36Sopenharmony_ci local->wowlan = false; 253062306a36Sopenharmony_ci if (res < 0) { 253162306a36Sopenharmony_ci local->resuming = false; 253262306a36Sopenharmony_ci return res; 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci if (res == 0) 253562306a36Sopenharmony_ci goto wake_up; 253662306a36Sopenharmony_ci WARN_ON(res > 1); 253762306a36Sopenharmony_ci /* 253862306a36Sopenharmony_ci * res is 1, which means the driver requested 253962306a36Sopenharmony_ci * to go through a regular reset on wakeup. 254062306a36Sopenharmony_ci * restore local->suspended in this case. 254162306a36Sopenharmony_ci */ 254262306a36Sopenharmony_ci reconfig_due_to_wowlan = true; 254362306a36Sopenharmony_ci local->suspended = true; 254462306a36Sopenharmony_ci } 254562306a36Sopenharmony_ci#endif 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci /* 254862306a36Sopenharmony_ci * In case of hw_restart during suspend (without wowlan), 254962306a36Sopenharmony_ci * cancel restart work, as we are reconfiguring the device 255062306a36Sopenharmony_ci * anyway. 255162306a36Sopenharmony_ci * Note that restart_work is scheduled on a frozen workqueue, 255262306a36Sopenharmony_ci * so we can't deadlock in this case. 255362306a36Sopenharmony_ci */ 255462306a36Sopenharmony_ci if (suspended && local->in_reconfig && !reconfig_due_to_wowlan) 255562306a36Sopenharmony_ci cancel_work_sync(&local->restart_work); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci local->started = false; 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci /* 256062306a36Sopenharmony_ci * Upon resume hardware can sometimes be goofy due to 256162306a36Sopenharmony_ci * various platform / driver / bus issues, so restarting 256262306a36Sopenharmony_ci * the device may at times not work immediately. Propagate 256362306a36Sopenharmony_ci * the error. 256462306a36Sopenharmony_ci */ 256562306a36Sopenharmony_ci res = drv_start(local); 256662306a36Sopenharmony_ci if (res) { 256762306a36Sopenharmony_ci if (suspended) 256862306a36Sopenharmony_ci WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); 256962306a36Sopenharmony_ci else 257062306a36Sopenharmony_ci WARN(1, "Hardware became unavailable during restart.\n"); 257162306a36Sopenharmony_ci ieee80211_handle_reconfig_failure(local); 257262306a36Sopenharmony_ci return res; 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci /* setup fragmentation threshold */ 257662306a36Sopenharmony_ci drv_set_frag_threshold(local, hw->wiphy->frag_threshold); 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci /* setup RTS threshold */ 257962306a36Sopenharmony_ci drv_set_rts_threshold(local, hw->wiphy->rts_threshold); 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci /* reset coverage class */ 258262306a36Sopenharmony_ci drv_set_coverage_class(local, hw->wiphy->coverage_class); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci ieee80211_led_radio(local, true); 258562306a36Sopenharmony_ci ieee80211_mod_tpt_led_trig(local, 258662306a36Sopenharmony_ci IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci /* add interfaces */ 258962306a36Sopenharmony_ci sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata); 259062306a36Sopenharmony_ci if (sdata) { 259162306a36Sopenharmony_ci /* in HW restart it exists already */ 259262306a36Sopenharmony_ci WARN_ON(local->resuming); 259362306a36Sopenharmony_ci res = drv_add_interface(local, sdata); 259462306a36Sopenharmony_ci if (WARN_ON(res)) { 259562306a36Sopenharmony_ci RCU_INIT_POINTER(local->monitor_sdata, NULL); 259662306a36Sopenharmony_ci synchronize_net(); 259762306a36Sopenharmony_ci kfree(sdata); 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci } 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 260262306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 260362306a36Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_MONITOR && 260462306a36Sopenharmony_ci ieee80211_sdata_running(sdata)) { 260562306a36Sopenharmony_ci res = drv_add_interface(local, sdata); 260662306a36Sopenharmony_ci if (WARN_ON(res)) 260762306a36Sopenharmony_ci break; 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci } 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci /* If adding any of the interfaces failed above, roll back and 261262306a36Sopenharmony_ci * report failure. 261362306a36Sopenharmony_ci */ 261462306a36Sopenharmony_ci if (res) { 261562306a36Sopenharmony_ci list_for_each_entry_continue_reverse(sdata, &local->interfaces, 261662306a36Sopenharmony_ci list) 261762306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 261862306a36Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_MONITOR && 261962306a36Sopenharmony_ci ieee80211_sdata_running(sdata)) 262062306a36Sopenharmony_ci drv_remove_interface(local, sdata); 262162306a36Sopenharmony_ci ieee80211_handle_reconfig_failure(local); 262262306a36Sopenharmony_ci return res; 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci /* add channel contexts */ 262662306a36Sopenharmony_ci if (local->use_chanctx) { 262762306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 262862306a36Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) 262962306a36Sopenharmony_ci if (ctx->replace_state != 263062306a36Sopenharmony_ci IEEE80211_CHANCTX_REPLACES_OTHER) 263162306a36Sopenharmony_ci WARN_ON(drv_add_chanctx(local, ctx)); 263262306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci sdata = wiphy_dereference(local->hw.wiphy, 263562306a36Sopenharmony_ci local->monitor_sdata); 263662306a36Sopenharmony_ci if (sdata && ieee80211_sdata_running(sdata)) 263762306a36Sopenharmony_ci ieee80211_assign_chanctx(local, sdata, &sdata->deflink); 263862306a36Sopenharmony_ci } 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci /* reconfigure hardware */ 264162306a36Sopenharmony_ci ieee80211_hw_config(local, ~0); 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci ieee80211_configure_filter(local); 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci /* Finally also reconfigure all the BSS information */ 264662306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 264762306a36Sopenharmony_ci /* common change flags for all interface types - link only */ 264862306a36Sopenharmony_ci u64 changed = BSS_CHANGED_ERP_CTS_PROT | 264962306a36Sopenharmony_ci BSS_CHANGED_ERP_PREAMBLE | 265062306a36Sopenharmony_ci BSS_CHANGED_ERP_SLOT | 265162306a36Sopenharmony_ci BSS_CHANGED_HT | 265262306a36Sopenharmony_ci BSS_CHANGED_BASIC_RATES | 265362306a36Sopenharmony_ci BSS_CHANGED_BEACON_INT | 265462306a36Sopenharmony_ci BSS_CHANGED_BSSID | 265562306a36Sopenharmony_ci BSS_CHANGED_CQM | 265662306a36Sopenharmony_ci BSS_CHANGED_QOS | 265762306a36Sopenharmony_ci BSS_CHANGED_TXPOWER | 265862306a36Sopenharmony_ci BSS_CHANGED_MCAST_RATE; 265962306a36Sopenharmony_ci struct ieee80211_link_data *link = NULL; 266062306a36Sopenharmony_ci unsigned int link_id; 266162306a36Sopenharmony_ci u32 active_links = 0; 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 266462306a36Sopenharmony_ci continue; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci sdata_lock(sdata); 266762306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 266862306a36Sopenharmony_ci struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS] = { 266962306a36Sopenharmony_ci [0] = &sdata->vif.bss_conf, 267062306a36Sopenharmony_ci }; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) { 267362306a36Sopenharmony_ci /* start with a single active link */ 267462306a36Sopenharmony_ci active_links = sdata->vif.active_links; 267562306a36Sopenharmony_ci link_id = ffs(active_links) - 1; 267662306a36Sopenharmony_ci sdata->vif.active_links = BIT(link_id); 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci drv_change_vif_links(local, sdata, 0, 268062306a36Sopenharmony_ci sdata->vif.active_links, 268162306a36Sopenharmony_ci old); 268262306a36Sopenharmony_ci } 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci for (link_id = 0; 268562306a36Sopenharmony_ci link_id < ARRAY_SIZE(sdata->vif.link_conf); 268662306a36Sopenharmony_ci link_id++) { 268762306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif) && 268862306a36Sopenharmony_ci !(sdata->vif.active_links & BIT(link_id))) 268962306a36Sopenharmony_ci continue; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci link = sdata_dereference(sdata->link[link_id], sdata); 269262306a36Sopenharmony_ci if (!link) 269362306a36Sopenharmony_ci continue; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci ieee80211_assign_chanctx(local, sdata, link); 269662306a36Sopenharmony_ci } 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_ci switch (sdata->vif.type) { 269962306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 270062306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 270162306a36Sopenharmony_ci break; 270262306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 270362306a36Sopenharmony_ci if (sdata->vif.cfg.ibss_joined) 270462306a36Sopenharmony_ci WARN_ON(drv_join_ibss(local, sdata)); 270562306a36Sopenharmony_ci fallthrough; 270662306a36Sopenharmony_ci default: 270762306a36Sopenharmony_ci ieee80211_reconfig_stations(sdata); 270862306a36Sopenharmony_ci fallthrough; 270962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: /* AP stations are handled later */ 271062306a36Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) 271162306a36Sopenharmony_ci drv_conf_tx(local, &sdata->deflink, i, 271262306a36Sopenharmony_ci &sdata->deflink.tx_conf[i]); 271362306a36Sopenharmony_ci break; 271462306a36Sopenharmony_ci } 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci if (sdata->vif.bss_conf.mu_mimo_owner) 271762306a36Sopenharmony_ci changed |= BSS_CHANGED_MU_GROUPS; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) 272062306a36Sopenharmony_ci changed |= BSS_CHANGED_IDLE; 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci switch (sdata->vif.type) { 272362306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 272462306a36Sopenharmony_ci if (!ieee80211_vif_is_mld(&sdata->vif)) { 272562306a36Sopenharmony_ci changed |= BSS_CHANGED_ASSOC | 272662306a36Sopenharmony_ci BSS_CHANGED_ARP_FILTER | 272762306a36Sopenharmony_ci BSS_CHANGED_PS; 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci /* Re-send beacon info report to the driver */ 273062306a36Sopenharmony_ci if (sdata->deflink.u.mgd.have_beacon) 273162306a36Sopenharmony_ci changed |= BSS_CHANGED_BEACON_INFO; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci if (sdata->vif.bss_conf.max_idle_period || 273462306a36Sopenharmony_ci sdata->vif.bss_conf.protected_keep_alive) 273562306a36Sopenharmony_ci changed |= BSS_CHANGED_KEEP_ALIVE; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci if (sdata->vif.bss_conf.eht_puncturing) 273862306a36Sopenharmony_ci changed |= BSS_CHANGED_EHT_PUNCTURING; 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, 274162306a36Sopenharmony_ci changed); 274262306a36Sopenharmony_ci } else if (!WARN_ON(!link)) { 274362306a36Sopenharmony_ci ieee80211_link_info_change_notify(sdata, link, 274462306a36Sopenharmony_ci changed); 274562306a36Sopenharmony_ci changed = BSS_CHANGED_ASSOC | 274662306a36Sopenharmony_ci BSS_CHANGED_IDLE | 274762306a36Sopenharmony_ci BSS_CHANGED_PS | 274862306a36Sopenharmony_ci BSS_CHANGED_ARP_FILTER; 274962306a36Sopenharmony_ci ieee80211_vif_cfg_change_notify(sdata, changed); 275062306a36Sopenharmony_ci } 275162306a36Sopenharmony_ci break; 275262306a36Sopenharmony_ci case NL80211_IFTYPE_OCB: 275362306a36Sopenharmony_ci changed |= BSS_CHANGED_OCB; 275462306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 275562306a36Sopenharmony_ci break; 275662306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 275762306a36Sopenharmony_ci changed |= BSS_CHANGED_IBSS; 275862306a36Sopenharmony_ci fallthrough; 275962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 276062306a36Sopenharmony_ci changed |= BSS_CHANGED_P2P_PS; 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) 276362306a36Sopenharmony_ci ieee80211_vif_cfg_change_notify(sdata, 276462306a36Sopenharmony_ci BSS_CHANGED_SSID); 276562306a36Sopenharmony_ci else 276662306a36Sopenharmony_ci changed |= BSS_CHANGED_SSID; 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci if (sdata->vif.bss_conf.ftm_responder == 1 && 276962306a36Sopenharmony_ci wiphy_ext_feature_isset(sdata->local->hw.wiphy, 277062306a36Sopenharmony_ci NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) 277162306a36Sopenharmony_ci changed |= BSS_CHANGED_FTM_RESPONDER; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) { 277462306a36Sopenharmony_ci changed |= BSS_CHANGED_AP_PROBE_RESP; 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci if (ieee80211_vif_is_mld(&sdata->vif)) { 277762306a36Sopenharmony_ci ieee80211_reconfig_ap_links(local, 277862306a36Sopenharmony_ci sdata, 277962306a36Sopenharmony_ci changed); 278062306a36Sopenharmony_ci break; 278162306a36Sopenharmony_ci } 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci if (rcu_access_pointer(sdata->deflink.u.ap.beacon)) 278462306a36Sopenharmony_ci drv_start_ap(local, sdata, 278562306a36Sopenharmony_ci sdata->deflink.conf); 278662306a36Sopenharmony_ci } 278762306a36Sopenharmony_ci fallthrough; 278862306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 278962306a36Sopenharmony_ci if (sdata->vif.bss_conf.enable_beacon) { 279062306a36Sopenharmony_ci changed |= BSS_CHANGED_BEACON | 279162306a36Sopenharmony_ci BSS_CHANGED_BEACON_ENABLED; 279262306a36Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci break; 279562306a36Sopenharmony_ci case NL80211_IFTYPE_NAN: 279662306a36Sopenharmony_ci res = ieee80211_reconfig_nan(sdata); 279762306a36Sopenharmony_ci if (res < 0) { 279862306a36Sopenharmony_ci sdata_unlock(sdata); 279962306a36Sopenharmony_ci ieee80211_handle_reconfig_failure(local); 280062306a36Sopenharmony_ci return res; 280162306a36Sopenharmony_ci } 280262306a36Sopenharmony_ci break; 280362306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 280462306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 280562306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 280662306a36Sopenharmony_ci /* nothing to do */ 280762306a36Sopenharmony_ci break; 280862306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 280962306a36Sopenharmony_ci case NUM_NL80211_IFTYPES: 281062306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 281162306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 281262306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 281362306a36Sopenharmony_ci WARN_ON(1); 281462306a36Sopenharmony_ci break; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci sdata_unlock(sdata); 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci if (active_links) 281962306a36Sopenharmony_ci ieee80211_set_active_links(&sdata->vif, active_links); 282062306a36Sopenharmony_ci } 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci ieee80211_recalc_ps(local); 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci /* 282562306a36Sopenharmony_ci * The sta might be in psm against the ap (e.g. because 282662306a36Sopenharmony_ci * this was the state before a hw restart), so we 282762306a36Sopenharmony_ci * explicitly send a null packet in order to make sure 282862306a36Sopenharmony_ci * it'll sync against the ap (and get out of psm). 282962306a36Sopenharmony_ci */ 283062306a36Sopenharmony_ci if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { 283162306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 283262306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION) 283362306a36Sopenharmony_ci continue; 283462306a36Sopenharmony_ci if (!sdata->u.mgd.associated) 283562306a36Sopenharmony_ci continue; 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci ieee80211_send_nullfunc(local, sdata, false); 283862306a36Sopenharmony_ci } 283962306a36Sopenharmony_ci } 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci /* APs are now beaconing, add back stations */ 284262306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 284362306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 284462306a36Sopenharmony_ci continue; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci sdata_lock(sdata); 284762306a36Sopenharmony_ci switch (sdata->vif.type) { 284862306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 284962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 285062306a36Sopenharmony_ci ieee80211_reconfig_stations(sdata); 285162306a36Sopenharmony_ci break; 285262306a36Sopenharmony_ci default: 285362306a36Sopenharmony_ci break; 285462306a36Sopenharmony_ci } 285562306a36Sopenharmony_ci sdata_unlock(sdata); 285662306a36Sopenharmony_ci } 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci /* add back keys */ 285962306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) 286062306a36Sopenharmony_ci ieee80211_reenable_keys(sdata); 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci /* Reconfigure sched scan if it was interrupted by FW restart */ 286362306a36Sopenharmony_ci mutex_lock(&local->mtx); 286462306a36Sopenharmony_ci sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, 286562306a36Sopenharmony_ci lockdep_is_held(&local->mtx)); 286662306a36Sopenharmony_ci sched_scan_req = rcu_dereference_protected(local->sched_scan_req, 286762306a36Sopenharmony_ci lockdep_is_held(&local->mtx)); 286862306a36Sopenharmony_ci if (sched_scan_sdata && sched_scan_req) 286962306a36Sopenharmony_ci /* 287062306a36Sopenharmony_ci * Sched scan stopped, but we don't want to report it. Instead, 287162306a36Sopenharmony_ci * we're trying to reschedule. However, if more than one scan 287262306a36Sopenharmony_ci * plan was set, we cannot reschedule since we don't know which 287362306a36Sopenharmony_ci * scan plan was currently running (and some scan plans may have 287462306a36Sopenharmony_ci * already finished). 287562306a36Sopenharmony_ci */ 287662306a36Sopenharmony_ci if (sched_scan_req->n_scan_plans > 1 || 287762306a36Sopenharmony_ci __ieee80211_request_sched_scan_start(sched_scan_sdata, 287862306a36Sopenharmony_ci sched_scan_req)) { 287962306a36Sopenharmony_ci RCU_INIT_POINTER(local->sched_scan_sdata, NULL); 288062306a36Sopenharmony_ci RCU_INIT_POINTER(local->sched_scan_req, NULL); 288162306a36Sopenharmony_ci sched_scan_stopped = true; 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci mutex_unlock(&local->mtx); 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci if (sched_scan_stopped) 288662306a36Sopenharmony_ci cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0); 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci wake_up: 288962306a36Sopenharmony_ci 289062306a36Sopenharmony_ci if (local->monitors == local->open_count && local->monitors > 0) 289162306a36Sopenharmony_ci ieee80211_add_virtual_monitor(local); 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci /* 289462306a36Sopenharmony_ci * Clear the WLAN_STA_BLOCK_BA flag so new aggregation 289562306a36Sopenharmony_ci * sessions can be established after a resume. 289662306a36Sopenharmony_ci * 289762306a36Sopenharmony_ci * Also tear down aggregation sessions since reconfiguring 289862306a36Sopenharmony_ci * them in a hardware restart scenario is not easily done 289962306a36Sopenharmony_ci * right now, and the hardware will have lost information 290062306a36Sopenharmony_ci * about the sessions, but we and the AP still think they 290162306a36Sopenharmony_ci * are active. This is really a workaround though. 290262306a36Sopenharmony_ci */ 290362306a36Sopenharmony_ci if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { 290462306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 290762306a36Sopenharmony_ci if (!local->resuming) 290862306a36Sopenharmony_ci ieee80211_sta_tear_down_BA_sessions( 290962306a36Sopenharmony_ci sta, AGG_STOP_LOCAL_REQUEST); 291062306a36Sopenharmony_ci clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 291162306a36Sopenharmony_ci } 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 291462306a36Sopenharmony_ci } 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci /* 291762306a36Sopenharmony_ci * If this is for hw restart things are still running. 291862306a36Sopenharmony_ci * We may want to change that later, however. 291962306a36Sopenharmony_ci */ 292062306a36Sopenharmony_ci if (local->open_count && (!suspended || reconfig_due_to_wowlan)) 292162306a36Sopenharmony_ci drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci if (local->in_reconfig) { 292462306a36Sopenharmony_ci in_reconfig = local->in_reconfig; 292562306a36Sopenharmony_ci local->in_reconfig = false; 292662306a36Sopenharmony_ci barrier(); 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci /* Restart deferred ROCs */ 292962306a36Sopenharmony_ci mutex_lock(&local->mtx); 293062306a36Sopenharmony_ci ieee80211_start_next_roc(local); 293162306a36Sopenharmony_ci mutex_unlock(&local->mtx); 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci /* Requeue all works */ 293462306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) 293562306a36Sopenharmony_ci wiphy_work_queue(local->hw.wiphy, &sdata->work); 293662306a36Sopenharmony_ci } 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 293962306a36Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_SUSPEND, 294062306a36Sopenharmony_ci false); 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci if (in_reconfig) { 294362306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 294462306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 294562306a36Sopenharmony_ci continue; 294662306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) 294762306a36Sopenharmony_ci ieee80211_sta_restart(sdata); 294862306a36Sopenharmony_ci } 294962306a36Sopenharmony_ci } 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci if (!suspended) 295262306a36Sopenharmony_ci return 0; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci#ifdef CONFIG_PM 295562306a36Sopenharmony_ci /* first set suspended false, then resuming */ 295662306a36Sopenharmony_ci local->suspended = false; 295762306a36Sopenharmony_ci mb(); 295862306a36Sopenharmony_ci local->resuming = false; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci ieee80211_flush_completed_scan(local, false); 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ci if (local->open_count && !reconfig_due_to_wowlan) 296362306a36Sopenharmony_ci drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 296662306a36Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 296762306a36Sopenharmony_ci continue; 296862306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) 296962306a36Sopenharmony_ci ieee80211_sta_restart(sdata); 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci mod_timer(&local->sta_cleanup, jiffies + 1); 297362306a36Sopenharmony_ci#else 297462306a36Sopenharmony_ci WARN_ON(1); 297562306a36Sopenharmony_ci#endif 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci return 0; 297862306a36Sopenharmony_ci} 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_cistatic void ieee80211_reconfig_disconnect(struct ieee80211_vif *vif, u8 flag) 298162306a36Sopenharmony_ci{ 298262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 298362306a36Sopenharmony_ci struct ieee80211_local *local; 298462306a36Sopenharmony_ci struct ieee80211_key *key; 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci if (WARN_ON(!vif)) 298762306a36Sopenharmony_ci return; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci sdata = vif_to_sdata(vif); 299062306a36Sopenharmony_ci local = sdata->local; 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_RESUME && 299362306a36Sopenharmony_ci !local->resuming)) 299462306a36Sopenharmony_ci return; 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci if (WARN_ON(flag & IEEE80211_SDATA_DISCONNECT_HW_RESTART && 299762306a36Sopenharmony_ci !local->in_reconfig)) 299862306a36Sopenharmony_ci return; 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) 300162306a36Sopenharmony_ci return; 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_ci sdata->flags |= flag; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci mutex_lock(&local->key_mtx); 300662306a36Sopenharmony_ci list_for_each_entry(key, &sdata->key_list, list) 300762306a36Sopenharmony_ci key->flags |= KEY_FLAG_TAINTED; 300862306a36Sopenharmony_ci mutex_unlock(&local->key_mtx); 300962306a36Sopenharmony_ci} 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_civoid ieee80211_hw_restart_disconnect(struct ieee80211_vif *vif) 301262306a36Sopenharmony_ci{ 301362306a36Sopenharmony_ci ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_HW_RESTART); 301462306a36Sopenharmony_ci} 301562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_hw_restart_disconnect); 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_civoid ieee80211_resume_disconnect(struct ieee80211_vif *vif) 301862306a36Sopenharmony_ci{ 301962306a36Sopenharmony_ci ieee80211_reconfig_disconnect(vif, IEEE80211_SDATA_DISCONNECT_RESUME); 302062306a36Sopenharmony_ci} 302162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_civoid ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata, 302462306a36Sopenharmony_ci struct ieee80211_link_data *link) 302562306a36Sopenharmony_ci{ 302662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 302762306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 302862306a36Sopenharmony_ci struct ieee80211_chanctx *chanctx; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci chanctx_conf = rcu_dereference_protected(link->conf->chanctx_conf, 303362306a36Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci /* 303662306a36Sopenharmony_ci * This function can be called from a work, thus it may be possible 303762306a36Sopenharmony_ci * that the chanctx_conf is removed (due to a disconnection, for 303862306a36Sopenharmony_ci * example). 303962306a36Sopenharmony_ci * So nothing should be done in such case. 304062306a36Sopenharmony_ci */ 304162306a36Sopenharmony_ci if (!chanctx_conf) 304262306a36Sopenharmony_ci goto unlock; 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 304562306a36Sopenharmony_ci ieee80211_recalc_smps_chanctx(local, chanctx); 304662306a36Sopenharmony_ci unlock: 304762306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 304862306a36Sopenharmony_ci} 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_civoid ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata, 305162306a36Sopenharmony_ci int link_id) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 305462306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 305562306a36Sopenharmony_ci struct ieee80211_chanctx *chanctx; 305662306a36Sopenharmony_ci int i; 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sdata->vif.link_conf); i++) { 306162306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci if (link_id >= 0 && link_id != i) 306462306a36Sopenharmony_ci continue; 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci rcu_read_lock(); 306762306a36Sopenharmony_ci bss_conf = rcu_dereference(sdata->vif.link_conf[i]); 306862306a36Sopenharmony_ci if (!bss_conf) { 306962306a36Sopenharmony_ci rcu_read_unlock(); 307062306a36Sopenharmony_ci continue; 307162306a36Sopenharmony_ci } 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci chanctx_conf = rcu_dereference_protected(bss_conf->chanctx_conf, 307462306a36Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 307562306a36Sopenharmony_ci /* 307662306a36Sopenharmony_ci * Since we hold the chanctx_mtx (checked above) 307762306a36Sopenharmony_ci * we can take the chanctx_conf pointer out of the 307862306a36Sopenharmony_ci * RCU critical section, it cannot go away without 307962306a36Sopenharmony_ci * the mutex. Just the way we reached it could - in 308062306a36Sopenharmony_ci * theory - go away, but we don't really care and 308162306a36Sopenharmony_ci * it really shouldn't happen anyway. 308262306a36Sopenharmony_ci */ 308362306a36Sopenharmony_ci rcu_read_unlock(); 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci if (!chanctx_conf) 308662306a36Sopenharmony_ci goto unlock; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, 308962306a36Sopenharmony_ci conf); 309062306a36Sopenharmony_ci ieee80211_recalc_chanctx_min_def(local, chanctx, NULL); 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci unlock: 309362306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 309462306a36Sopenharmony_ci} 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_cisize_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) 309762306a36Sopenharmony_ci{ 309862306a36Sopenharmony_ci size_t pos = offset; 309962306a36Sopenharmony_ci 310062306a36Sopenharmony_ci while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) 310162306a36Sopenharmony_ci pos += 2 + ies[pos + 1]; 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci return pos; 310462306a36Sopenharmony_ci} 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ciu8 *ieee80211_ie_build_s1g_cap(u8 *pos, struct ieee80211_sta_s1g_cap *s1g_cap) 310762306a36Sopenharmony_ci{ 310862306a36Sopenharmony_ci *pos++ = WLAN_EID_S1G_CAPABILITIES; 310962306a36Sopenharmony_ci *pos++ = sizeof(struct ieee80211_s1g_cap); 311062306a36Sopenharmony_ci memset(pos, 0, sizeof(struct ieee80211_s1g_cap)); 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci memcpy(pos, &s1g_cap->cap, sizeof(s1g_cap->cap)); 311362306a36Sopenharmony_ci pos += sizeof(s1g_cap->cap); 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci memcpy(pos, &s1g_cap->nss_mcs, sizeof(s1g_cap->nss_mcs)); 311662306a36Sopenharmony_ci pos += sizeof(s1g_cap->nss_mcs); 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci return pos; 311962306a36Sopenharmony_ci} 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ciu8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 312262306a36Sopenharmony_ci u16 cap) 312362306a36Sopenharmony_ci{ 312462306a36Sopenharmony_ci __le16 tmp; 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_ci *pos++ = WLAN_EID_HT_CAPABILITY; 312762306a36Sopenharmony_ci *pos++ = sizeof(struct ieee80211_ht_cap); 312862306a36Sopenharmony_ci memset(pos, 0, sizeof(struct ieee80211_ht_cap)); 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci /* capability flags */ 313162306a36Sopenharmony_ci tmp = cpu_to_le16(cap); 313262306a36Sopenharmony_ci memcpy(pos, &tmp, sizeof(u16)); 313362306a36Sopenharmony_ci pos += sizeof(u16); 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci /* AMPDU parameters */ 313662306a36Sopenharmony_ci *pos++ = ht_cap->ampdu_factor | 313762306a36Sopenharmony_ci (ht_cap->ampdu_density << 313862306a36Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci /* MCS set */ 314162306a36Sopenharmony_ci memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); 314262306a36Sopenharmony_ci pos += sizeof(ht_cap->mcs); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci /* extended capabilities */ 314562306a36Sopenharmony_ci pos += sizeof(__le16); 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci /* BF capabilities */ 314862306a36Sopenharmony_ci pos += sizeof(__le32); 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci /* antenna selection */ 315162306a36Sopenharmony_ci pos += sizeof(u8); 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci return pos; 315462306a36Sopenharmony_ci} 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ciu8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 315762306a36Sopenharmony_ci u32 cap) 315862306a36Sopenharmony_ci{ 315962306a36Sopenharmony_ci __le32 tmp; 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci *pos++ = WLAN_EID_VHT_CAPABILITY; 316262306a36Sopenharmony_ci *pos++ = sizeof(struct ieee80211_vht_cap); 316362306a36Sopenharmony_ci memset(pos, 0, sizeof(struct ieee80211_vht_cap)); 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci /* capability flags */ 316662306a36Sopenharmony_ci tmp = cpu_to_le32(cap); 316762306a36Sopenharmony_ci memcpy(pos, &tmp, sizeof(u32)); 316862306a36Sopenharmony_ci pos += sizeof(u32); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci /* VHT MCS set */ 317162306a36Sopenharmony_ci memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); 317262306a36Sopenharmony_ci pos += sizeof(vht_cap->vht_mcs); 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci return pos; 317562306a36Sopenharmony_ci} 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ciu8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) 317862306a36Sopenharmony_ci{ 317962306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 318062306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 318162306a36Sopenharmony_ci u8 n; 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci sband = ieee80211_get_sband(sdata); 318462306a36Sopenharmony_ci if (!sband) 318562306a36Sopenharmony_ci return 0; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 318862306a36Sopenharmony_ci if (!he_cap) 318962306a36Sopenharmony_ci return 0; 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); 319262306a36Sopenharmony_ci return 2 + 1 + 319362306a36Sopenharmony_ci sizeof(he_cap->he_cap_elem) + n + 319462306a36Sopenharmony_ci ieee80211_he_ppe_size(he_cap->ppe_thres[0], 319562306a36Sopenharmony_ci he_cap->he_cap_elem.phy_cap_info); 319662306a36Sopenharmony_ci} 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ciu8 *ieee80211_ie_build_he_cap(ieee80211_conn_flags_t disable_flags, u8 *pos, 319962306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap, 320062306a36Sopenharmony_ci u8 *end) 320162306a36Sopenharmony_ci{ 320262306a36Sopenharmony_ci struct ieee80211_he_cap_elem elem; 320362306a36Sopenharmony_ci u8 n; 320462306a36Sopenharmony_ci u8 ie_len; 320562306a36Sopenharmony_ci u8 *orig_pos = pos; 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci /* Make sure we have place for the IE */ 320862306a36Sopenharmony_ci /* 320962306a36Sopenharmony_ci * TODO: the 1 added is because this temporarily is under the EXTENSION 321062306a36Sopenharmony_ci * IE. Get rid of it when it moves. 321162306a36Sopenharmony_ci */ 321262306a36Sopenharmony_ci if (!he_cap) 321362306a36Sopenharmony_ci return orig_pos; 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci /* modify on stack first to calculate 'n' and 'ie_len' correctly */ 321662306a36Sopenharmony_ci elem = he_cap->he_cap_elem; 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci if (disable_flags & IEEE80211_CONN_DISABLE_40MHZ) 321962306a36Sopenharmony_ci elem.phy_cap_info[0] &= 322062306a36Sopenharmony_ci ~(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | 322162306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G); 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_ci if (disable_flags & IEEE80211_CONN_DISABLE_160MHZ) 322462306a36Sopenharmony_ci elem.phy_cap_info[0] &= 322562306a36Sopenharmony_ci ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci if (disable_flags & IEEE80211_CONN_DISABLE_80P80MHZ) 322862306a36Sopenharmony_ci elem.phy_cap_info[0] &= 322962306a36Sopenharmony_ci ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_ci n = ieee80211_he_mcs_nss_size(&elem); 323262306a36Sopenharmony_ci ie_len = 2 + 1 + 323362306a36Sopenharmony_ci sizeof(he_cap->he_cap_elem) + n + 323462306a36Sopenharmony_ci ieee80211_he_ppe_size(he_cap->ppe_thres[0], 323562306a36Sopenharmony_ci he_cap->he_cap_elem.phy_cap_info); 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci if ((end - pos) < ie_len) 323862306a36Sopenharmony_ci return orig_pos; 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 324162306a36Sopenharmony_ci pos++; /* We'll set the size later below */ 324262306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_HE_CAPABILITY; 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ci /* Fixed data */ 324562306a36Sopenharmony_ci memcpy(pos, &elem, sizeof(elem)); 324662306a36Sopenharmony_ci pos += sizeof(elem); 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci memcpy(pos, &he_cap->he_mcs_nss_supp, n); 324962306a36Sopenharmony_ci pos += n; 325062306a36Sopenharmony_ci 325162306a36Sopenharmony_ci /* Check if PPE Threshold should be present */ 325262306a36Sopenharmony_ci if ((he_cap->he_cap_elem.phy_cap_info[6] & 325362306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0) 325462306a36Sopenharmony_ci goto end; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci /* 325762306a36Sopenharmony_ci * Calculate how many PPET16/PPET8 pairs are to come. Algorithm: 325862306a36Sopenharmony_ci * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK) 325962306a36Sopenharmony_ci */ 326062306a36Sopenharmony_ci n = hweight8(he_cap->ppe_thres[0] & 326162306a36Sopenharmony_ci IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); 326262306a36Sopenharmony_ci n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >> 326362306a36Sopenharmony_ci IEEE80211_PPE_THRES_NSS_POS)); 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci /* 326662306a36Sopenharmony_ci * Each pair is 6 bits, and we need to add the 7 "header" bits to the 326762306a36Sopenharmony_ci * total size. 326862306a36Sopenharmony_ci */ 326962306a36Sopenharmony_ci n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7; 327062306a36Sopenharmony_ci n = DIV_ROUND_UP(n, 8); 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci /* Copy PPE Thresholds */ 327362306a36Sopenharmony_ci memcpy(pos, &he_cap->ppe_thres, n); 327462306a36Sopenharmony_ci pos += n; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ciend: 327762306a36Sopenharmony_ci orig_pos[1] = (pos - orig_pos) - 2; 327862306a36Sopenharmony_ci return pos; 327962306a36Sopenharmony_ci} 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_civoid ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, 328262306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode, 328362306a36Sopenharmony_ci struct sk_buff *skb) 328462306a36Sopenharmony_ci{ 328562306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 328662306a36Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 328762306a36Sopenharmony_ci enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 328862306a36Sopenharmony_ci u8 *pos; 328962306a36Sopenharmony_ci u16 cap; 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (!cfg80211_any_usable_channels(sdata->local->hw.wiphy, 329262306a36Sopenharmony_ci BIT(NL80211_BAND_6GHZ), 329362306a36Sopenharmony_ci IEEE80211_CHAN_NO_HE)) 329462306a36Sopenharmony_ci return; 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci sband = sdata->local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci iftd = ieee80211_get_sband_iftype_data(sband, iftype); 329962306a36Sopenharmony_ci if (!iftd) 330062306a36Sopenharmony_ci return; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci /* Check for device HE 6 GHz capability before adding element */ 330362306a36Sopenharmony_ci if (!iftd->he_6ghz_capa.capa) 330462306a36Sopenharmony_ci return; 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci cap = le16_to_cpu(iftd->he_6ghz_capa.capa); 330762306a36Sopenharmony_ci cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS; 330862306a36Sopenharmony_ci 330962306a36Sopenharmony_ci switch (smps_mode) { 331062306a36Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 331162306a36Sopenharmony_ci case IEEE80211_SMPS_NUM_MODES: 331262306a36Sopenharmony_ci WARN_ON(1); 331362306a36Sopenharmony_ci fallthrough; 331462306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 331562306a36Sopenharmony_ci cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED, 331662306a36Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_SM_PS); 331762306a36Sopenharmony_ci break; 331862306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 331962306a36Sopenharmony_ci cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC, 332062306a36Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_SM_PS); 332162306a36Sopenharmony_ci break; 332262306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 332362306a36Sopenharmony_ci cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC, 332462306a36Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_SM_PS); 332562306a36Sopenharmony_ci break; 332662306a36Sopenharmony_ci } 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci pos = skb_put(skb, 2 + 1 + sizeof(cap)); 332962306a36Sopenharmony_ci ieee80211_write_he_6ghz_cap(pos, cpu_to_le16(cap), 333062306a36Sopenharmony_ci pos + 2 + 1 + sizeof(cap)); 333162306a36Sopenharmony_ci} 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ciu8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 333462306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef, 333562306a36Sopenharmony_ci u16 prot_mode, bool rifs_mode) 333662306a36Sopenharmony_ci{ 333762306a36Sopenharmony_ci struct ieee80211_ht_operation *ht_oper; 333862306a36Sopenharmony_ci /* Build HT Information */ 333962306a36Sopenharmony_ci *pos++ = WLAN_EID_HT_OPERATION; 334062306a36Sopenharmony_ci *pos++ = sizeof(struct ieee80211_ht_operation); 334162306a36Sopenharmony_ci ht_oper = (struct ieee80211_ht_operation *)pos; 334262306a36Sopenharmony_ci ht_oper->primary_chan = ieee80211_frequency_to_channel( 334362306a36Sopenharmony_ci chandef->chan->center_freq); 334462306a36Sopenharmony_ci switch (chandef->width) { 334562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 334662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 334762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 334862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 334962306a36Sopenharmony_ci if (chandef->center_freq1 > chandef->chan->center_freq) 335062306a36Sopenharmony_ci ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 335162306a36Sopenharmony_ci else 335262306a36Sopenharmony_ci ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 335362306a36Sopenharmony_ci break; 335462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 335562306a36Sopenharmony_ci /* HT information element should not be included on 6GHz */ 335662306a36Sopenharmony_ci WARN_ON(1); 335762306a36Sopenharmony_ci return pos; 335862306a36Sopenharmony_ci default: 335962306a36Sopenharmony_ci ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; 336062306a36Sopenharmony_ci break; 336162306a36Sopenharmony_ci } 336262306a36Sopenharmony_ci if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && 336362306a36Sopenharmony_ci chandef->width != NL80211_CHAN_WIDTH_20_NOHT && 336462306a36Sopenharmony_ci chandef->width != NL80211_CHAN_WIDTH_20) 336562306a36Sopenharmony_ci ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci if (rifs_mode) 336862306a36Sopenharmony_ci ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE; 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci ht_oper->operation_mode = cpu_to_le16(prot_mode); 337162306a36Sopenharmony_ci ht_oper->stbc_param = 0x0000; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci /* It seems that Basic MCS set and Supported MCS set 337462306a36Sopenharmony_ci are identical for the first 10 bytes */ 337562306a36Sopenharmony_ci memset(&ht_oper->basic_set, 0, 16); 337662306a36Sopenharmony_ci memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci return pos + sizeof(struct ieee80211_ht_operation); 337962306a36Sopenharmony_ci} 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_civoid ieee80211_ie_build_wide_bw_cs(u8 *pos, 338262306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef) 338362306a36Sopenharmony_ci{ 338462306a36Sopenharmony_ci *pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH; /* EID */ 338562306a36Sopenharmony_ci *pos++ = 3; /* IE length */ 338662306a36Sopenharmony_ci /* New channel width */ 338762306a36Sopenharmony_ci switch (chandef->width) { 338862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 338962306a36Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ; 339062306a36Sopenharmony_ci break; 339162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 339262306a36Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ; 339362306a36Sopenharmony_ci break; 339462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 339562306a36Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ; 339662306a36Sopenharmony_ci break; 339762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 339862306a36Sopenharmony_ci /* The behavior is not defined for 320 MHz channels */ 339962306a36Sopenharmony_ci WARN_ON(1); 340062306a36Sopenharmony_ci fallthrough; 340162306a36Sopenharmony_ci default: 340262306a36Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT; 340362306a36Sopenharmony_ci } 340462306a36Sopenharmony_ci 340562306a36Sopenharmony_ci /* new center frequency segment 0 */ 340662306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(chandef->center_freq1); 340762306a36Sopenharmony_ci /* new center frequency segment 1 */ 340862306a36Sopenharmony_ci if (chandef->center_freq2) 340962306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(chandef->center_freq2); 341062306a36Sopenharmony_ci else 341162306a36Sopenharmony_ci *pos++ = 0; 341262306a36Sopenharmony_ci} 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ciu8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 341562306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef) 341662306a36Sopenharmony_ci{ 341762306a36Sopenharmony_ci struct ieee80211_vht_operation *vht_oper; 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci *pos++ = WLAN_EID_VHT_OPERATION; 342062306a36Sopenharmony_ci *pos++ = sizeof(struct ieee80211_vht_operation); 342162306a36Sopenharmony_ci vht_oper = (struct ieee80211_vht_operation *)pos; 342262306a36Sopenharmony_ci vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel( 342362306a36Sopenharmony_ci chandef->center_freq1); 342462306a36Sopenharmony_ci if (chandef->center_freq2) 342562306a36Sopenharmony_ci vht_oper->center_freq_seg1_idx = 342662306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq2); 342762306a36Sopenharmony_ci else 342862306a36Sopenharmony_ci vht_oper->center_freq_seg1_idx = 0x00; 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci switch (chandef->width) { 343162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 343262306a36Sopenharmony_ci /* 343362306a36Sopenharmony_ci * Convert 160 MHz channel width to new style as interop 343462306a36Sopenharmony_ci * workaround. 343562306a36Sopenharmony_ci */ 343662306a36Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 343762306a36Sopenharmony_ci vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx; 343862306a36Sopenharmony_ci if (chandef->chan->center_freq < chandef->center_freq1) 343962306a36Sopenharmony_ci vht_oper->center_freq_seg0_idx -= 8; 344062306a36Sopenharmony_ci else 344162306a36Sopenharmony_ci vht_oper->center_freq_seg0_idx += 8; 344262306a36Sopenharmony_ci break; 344362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 344462306a36Sopenharmony_ci /* 344562306a36Sopenharmony_ci * Convert 80+80 MHz channel width to new style as interop 344662306a36Sopenharmony_ci * workaround. 344762306a36Sopenharmony_ci */ 344862306a36Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 344962306a36Sopenharmony_ci break; 345062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 345162306a36Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 345262306a36Sopenharmony_ci break; 345362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 345462306a36Sopenharmony_ci /* VHT information element should not be included on 6GHz */ 345562306a36Sopenharmony_ci WARN_ON(1); 345662306a36Sopenharmony_ci return pos; 345762306a36Sopenharmony_ci default: 345862306a36Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; 345962306a36Sopenharmony_ci break; 346062306a36Sopenharmony_ci } 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci /* don't require special VHT peer rates */ 346362306a36Sopenharmony_ci vht_oper->basic_mcs_set = cpu_to_le16(0xffff); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci return pos + sizeof(struct ieee80211_vht_operation); 346662306a36Sopenharmony_ci} 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ciu8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef) 346962306a36Sopenharmony_ci{ 347062306a36Sopenharmony_ci struct ieee80211_he_operation *he_oper; 347162306a36Sopenharmony_ci struct ieee80211_he_6ghz_oper *he_6ghz_op; 347262306a36Sopenharmony_ci u32 he_oper_params; 347362306a36Sopenharmony_ci u8 ie_len = 1 + sizeof(struct ieee80211_he_operation); 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 347662306a36Sopenharmony_ci ie_len += sizeof(struct ieee80211_he_6ghz_oper); 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 347962306a36Sopenharmony_ci *pos++ = ie_len; 348062306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_HE_OPERATION; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci he_oper_params = 0; 348362306a36Sopenharmony_ci he_oper_params |= u32_encode_bits(1023, /* disabled */ 348462306a36Sopenharmony_ci IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); 348562306a36Sopenharmony_ci he_oper_params |= u32_encode_bits(1, 348662306a36Sopenharmony_ci IEEE80211_HE_OPERATION_ER_SU_DISABLE); 348762306a36Sopenharmony_ci he_oper_params |= u32_encode_bits(1, 348862306a36Sopenharmony_ci IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED); 348962306a36Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 349062306a36Sopenharmony_ci he_oper_params |= u32_encode_bits(1, 349162306a36Sopenharmony_ci IEEE80211_HE_OPERATION_6GHZ_OP_INFO); 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci he_oper = (struct ieee80211_he_operation *)pos; 349462306a36Sopenharmony_ci he_oper->he_oper_params = cpu_to_le32(he_oper_params); 349562306a36Sopenharmony_ci 349662306a36Sopenharmony_ci /* don't require special HE peer rates */ 349762306a36Sopenharmony_ci he_oper->he_mcs_nss_set = cpu_to_le16(0xffff); 349862306a36Sopenharmony_ci pos += sizeof(struct ieee80211_he_operation); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_6GHZ) 350162306a36Sopenharmony_ci goto out; 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_ci /* TODO add VHT operational */ 350462306a36Sopenharmony_ci he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos; 350562306a36Sopenharmony_ci he_6ghz_op->minrate = 6; /* 6 Mbps */ 350662306a36Sopenharmony_ci he_6ghz_op->primary = 350762306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->chan->center_freq); 350862306a36Sopenharmony_ci he_6ghz_op->ccfs0 = 350962306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq1); 351062306a36Sopenharmony_ci if (chandef->center_freq2) 351162306a36Sopenharmony_ci he_6ghz_op->ccfs1 = 351262306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq2); 351362306a36Sopenharmony_ci else 351462306a36Sopenharmony_ci he_6ghz_op->ccfs1 = 0; 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci switch (chandef->width) { 351762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 351862306a36Sopenharmony_ci /* 351962306a36Sopenharmony_ci * TODO: mesh operation is not defined over 6GHz 320 MHz 352062306a36Sopenharmony_ci * channels. 352162306a36Sopenharmony_ci */ 352262306a36Sopenharmony_ci WARN_ON(1); 352362306a36Sopenharmony_ci break; 352462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 352562306a36Sopenharmony_ci /* Convert 160 MHz channel width to new style as interop 352662306a36Sopenharmony_ci * workaround. 352762306a36Sopenharmony_ci */ 352862306a36Sopenharmony_ci he_6ghz_op->control = 352962306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; 353062306a36Sopenharmony_ci he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0; 353162306a36Sopenharmony_ci if (chandef->chan->center_freq < chandef->center_freq1) 353262306a36Sopenharmony_ci he_6ghz_op->ccfs0 -= 8; 353362306a36Sopenharmony_ci else 353462306a36Sopenharmony_ci he_6ghz_op->ccfs0 += 8; 353562306a36Sopenharmony_ci fallthrough; 353662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 353762306a36Sopenharmony_ci he_6ghz_op->control = 353862306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; 353962306a36Sopenharmony_ci break; 354062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 354162306a36Sopenharmony_ci he_6ghz_op->control = 354262306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ; 354362306a36Sopenharmony_ci break; 354462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 354562306a36Sopenharmony_ci he_6ghz_op->control = 354662306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ; 354762306a36Sopenharmony_ci break; 354862306a36Sopenharmony_ci default: 354962306a36Sopenharmony_ci he_6ghz_op->control = 355062306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ; 355162306a36Sopenharmony_ci break; 355262306a36Sopenharmony_ci } 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci pos += sizeof(struct ieee80211_he_6ghz_oper); 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ciout: 355762306a36Sopenharmony_ci return pos; 355862306a36Sopenharmony_ci} 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ciu8 *ieee80211_ie_build_eht_oper(u8 *pos, struct cfg80211_chan_def *chandef, 356162306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap) 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci{ 356462306a36Sopenharmony_ci const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss = 356562306a36Sopenharmony_ci &eht_cap->eht_mcs_nss_supp.only_20mhz; 356662306a36Sopenharmony_ci struct ieee80211_eht_operation *eht_oper; 356762306a36Sopenharmony_ci struct ieee80211_eht_operation_info *eht_oper_info; 356862306a36Sopenharmony_ci u8 eht_oper_len = offsetof(struct ieee80211_eht_operation, optional); 356962306a36Sopenharmony_ci u8 eht_oper_info_len = 357062306a36Sopenharmony_ci offsetof(struct ieee80211_eht_operation_info, optional); 357162306a36Sopenharmony_ci u8 chan_width = 0; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 357462306a36Sopenharmony_ci *pos++ = 1 + eht_oper_len + eht_oper_info_len; 357562306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_EHT_OPERATION; 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_ci eht_oper = (struct ieee80211_eht_operation *)pos; 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci memcpy(&eht_oper->basic_mcs_nss, eht_mcs_nss, sizeof(*eht_mcs_nss)); 358062306a36Sopenharmony_ci eht_oper->params |= IEEE80211_EHT_OPER_INFO_PRESENT; 358162306a36Sopenharmony_ci pos += eht_oper_len; 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci eht_oper_info = 358462306a36Sopenharmony_ci (struct ieee80211_eht_operation_info *)eht_oper->optional; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci eht_oper_info->ccfs0 = 358762306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq1); 358862306a36Sopenharmony_ci if (chandef->center_freq2) 358962306a36Sopenharmony_ci eht_oper_info->ccfs1 = 359062306a36Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq2); 359162306a36Sopenharmony_ci else 359262306a36Sopenharmony_ci eht_oper_info->ccfs1 = 0; 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci switch (chandef->width) { 359562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 359662306a36Sopenharmony_ci chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ; 359762306a36Sopenharmony_ci eht_oper_info->ccfs1 = eht_oper_info->ccfs0; 359862306a36Sopenharmony_ci if (chandef->chan->center_freq < chandef->center_freq1) 359962306a36Sopenharmony_ci eht_oper_info->ccfs0 -= 16; 360062306a36Sopenharmony_ci else 360162306a36Sopenharmony_ci eht_oper_info->ccfs0 += 16; 360262306a36Sopenharmony_ci break; 360362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 360462306a36Sopenharmony_ci eht_oper_info->ccfs1 = eht_oper_info->ccfs0; 360562306a36Sopenharmony_ci if (chandef->chan->center_freq < chandef->center_freq1) 360662306a36Sopenharmony_ci eht_oper_info->ccfs0 -= 8; 360762306a36Sopenharmony_ci else 360862306a36Sopenharmony_ci eht_oper_info->ccfs0 += 8; 360962306a36Sopenharmony_ci fallthrough; 361062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 361162306a36Sopenharmony_ci chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ; 361262306a36Sopenharmony_ci break; 361362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 361462306a36Sopenharmony_ci chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ; 361562306a36Sopenharmony_ci break; 361662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 361762306a36Sopenharmony_ci chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ; 361862306a36Sopenharmony_ci break; 361962306a36Sopenharmony_ci default: 362062306a36Sopenharmony_ci chan_width = IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ; 362162306a36Sopenharmony_ci break; 362262306a36Sopenharmony_ci } 362362306a36Sopenharmony_ci eht_oper_info->control = chan_width; 362462306a36Sopenharmony_ci pos += eht_oper_info_len; 362562306a36Sopenharmony_ci 362662306a36Sopenharmony_ci /* TODO: eht_oper_info->optional */ 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci return pos; 362962306a36Sopenharmony_ci} 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_cibool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, 363262306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 363362306a36Sopenharmony_ci{ 363462306a36Sopenharmony_ci enum nl80211_channel_type channel_type; 363562306a36Sopenharmony_ci 363662306a36Sopenharmony_ci if (!ht_oper) 363762306a36Sopenharmony_ci return false; 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 364062306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_NONE: 364162306a36Sopenharmony_ci channel_type = NL80211_CHAN_HT20; 364262306a36Sopenharmony_ci break; 364362306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 364462306a36Sopenharmony_ci channel_type = NL80211_CHAN_HT40PLUS; 364562306a36Sopenharmony_ci break; 364662306a36Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 364762306a36Sopenharmony_ci channel_type = NL80211_CHAN_HT40MINUS; 364862306a36Sopenharmony_ci break; 364962306a36Sopenharmony_ci default: 365062306a36Sopenharmony_ci return false; 365162306a36Sopenharmony_ci } 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci cfg80211_chandef_create(chandef, chandef->chan, channel_type); 365462306a36Sopenharmony_ci return true; 365562306a36Sopenharmony_ci} 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_cibool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, 365862306a36Sopenharmony_ci const struct ieee80211_vht_operation *oper, 365962306a36Sopenharmony_ci const struct ieee80211_ht_operation *htop, 366062306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 366162306a36Sopenharmony_ci{ 366262306a36Sopenharmony_ci struct cfg80211_chan_def new = *chandef; 366362306a36Sopenharmony_ci int cf0, cf1; 366462306a36Sopenharmony_ci int ccfs0, ccfs1, ccfs2; 366562306a36Sopenharmony_ci int ccf0, ccf1; 366662306a36Sopenharmony_ci u32 vht_cap; 366762306a36Sopenharmony_ci bool support_80_80 = false; 366862306a36Sopenharmony_ci bool support_160 = false; 366962306a36Sopenharmony_ci u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info, 367062306a36Sopenharmony_ci IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); 367162306a36Sopenharmony_ci u8 supp_chwidth = u32_get_bits(vht_cap_info, 367262306a36Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK); 367362306a36Sopenharmony_ci 367462306a36Sopenharmony_ci if (!oper || !htop) 367562306a36Sopenharmony_ci return false; 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci vht_cap = hw->wiphy->bands[chandef->chan->band]->vht_cap.cap; 367862306a36Sopenharmony_ci support_160 = (vht_cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK | 367962306a36Sopenharmony_ci IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)); 368062306a36Sopenharmony_ci support_80_80 = ((vht_cap & 368162306a36Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) || 368262306a36Sopenharmony_ci (vht_cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && 368362306a36Sopenharmony_ci vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) || 368462306a36Sopenharmony_ci ((vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) >> 368562306a36Sopenharmony_ci IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT > 1)); 368662306a36Sopenharmony_ci ccfs0 = oper->center_freq_seg0_idx; 368762306a36Sopenharmony_ci ccfs1 = oper->center_freq_seg1_idx; 368862306a36Sopenharmony_ci ccfs2 = (le16_to_cpu(htop->operation_mode) & 368962306a36Sopenharmony_ci IEEE80211_HT_OP_MODE_CCFS2_MASK) 369062306a36Sopenharmony_ci >> IEEE80211_HT_OP_MODE_CCFS2_SHIFT; 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_ci ccf0 = ccfs0; 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci /* if not supported, parse as though we didn't understand it */ 369562306a36Sopenharmony_ci if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW)) 369662306a36Sopenharmony_ci ext_nss_bw_supp = 0; 369762306a36Sopenharmony_ci 369862306a36Sopenharmony_ci /* 369962306a36Sopenharmony_ci * Cf. IEEE 802.11 Table 9-250 370062306a36Sopenharmony_ci * 370162306a36Sopenharmony_ci * We really just consider that because it's inefficient to connect 370262306a36Sopenharmony_ci * at a higher bandwidth than we'll actually be able to use. 370362306a36Sopenharmony_ci */ 370462306a36Sopenharmony_ci switch ((supp_chwidth << 4) | ext_nss_bw_supp) { 370562306a36Sopenharmony_ci default: 370662306a36Sopenharmony_ci case 0x00: 370762306a36Sopenharmony_ci ccf1 = 0; 370862306a36Sopenharmony_ci support_160 = false; 370962306a36Sopenharmony_ci support_80_80 = false; 371062306a36Sopenharmony_ci break; 371162306a36Sopenharmony_ci case 0x01: 371262306a36Sopenharmony_ci support_80_80 = false; 371362306a36Sopenharmony_ci fallthrough; 371462306a36Sopenharmony_ci case 0x02: 371562306a36Sopenharmony_ci case 0x03: 371662306a36Sopenharmony_ci ccf1 = ccfs2; 371762306a36Sopenharmony_ci break; 371862306a36Sopenharmony_ci case 0x10: 371962306a36Sopenharmony_ci ccf1 = ccfs1; 372062306a36Sopenharmony_ci break; 372162306a36Sopenharmony_ci case 0x11: 372262306a36Sopenharmony_ci case 0x12: 372362306a36Sopenharmony_ci if (!ccfs1) 372462306a36Sopenharmony_ci ccf1 = ccfs2; 372562306a36Sopenharmony_ci else 372662306a36Sopenharmony_ci ccf1 = ccfs1; 372762306a36Sopenharmony_ci break; 372862306a36Sopenharmony_ci case 0x13: 372962306a36Sopenharmony_ci case 0x20: 373062306a36Sopenharmony_ci case 0x23: 373162306a36Sopenharmony_ci ccf1 = ccfs1; 373262306a36Sopenharmony_ci break; 373362306a36Sopenharmony_ci } 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band); 373662306a36Sopenharmony_ci cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band); 373762306a36Sopenharmony_ci 373862306a36Sopenharmony_ci switch (oper->chan_width) { 373962306a36Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_USE_HT: 374062306a36Sopenharmony_ci /* just use HT information directly */ 374162306a36Sopenharmony_ci break; 374262306a36Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_80MHZ: 374362306a36Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_80; 374462306a36Sopenharmony_ci new.center_freq1 = cf0; 374562306a36Sopenharmony_ci /* If needed, adjust based on the newer interop workaround. */ 374662306a36Sopenharmony_ci if (ccf1) { 374762306a36Sopenharmony_ci unsigned int diff; 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_ci diff = abs(ccf1 - ccf0); 375062306a36Sopenharmony_ci if ((diff == 8) && support_160) { 375162306a36Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_160; 375262306a36Sopenharmony_ci new.center_freq1 = cf1; 375362306a36Sopenharmony_ci } else if ((diff > 8) && support_80_80) { 375462306a36Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_80P80; 375562306a36Sopenharmony_ci new.center_freq2 = cf1; 375662306a36Sopenharmony_ci } 375762306a36Sopenharmony_ci } 375862306a36Sopenharmony_ci break; 375962306a36Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_160MHZ: 376062306a36Sopenharmony_ci /* deprecated encoding */ 376162306a36Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_160; 376262306a36Sopenharmony_ci new.center_freq1 = cf0; 376362306a36Sopenharmony_ci break; 376462306a36Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_80P80MHZ: 376562306a36Sopenharmony_ci /* deprecated encoding */ 376662306a36Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_80P80; 376762306a36Sopenharmony_ci new.center_freq1 = cf0; 376862306a36Sopenharmony_ci new.center_freq2 = cf1; 376962306a36Sopenharmony_ci break; 377062306a36Sopenharmony_ci default: 377162306a36Sopenharmony_ci return false; 377262306a36Sopenharmony_ci } 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_ci if (!cfg80211_chandef_valid(&new)) 377562306a36Sopenharmony_ci return false; 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci *chandef = new; 377862306a36Sopenharmony_ci return true; 377962306a36Sopenharmony_ci} 378062306a36Sopenharmony_ci 378162306a36Sopenharmony_civoid ieee80211_chandef_eht_oper(const struct ieee80211_eht_operation *eht_oper, 378262306a36Sopenharmony_ci bool support_160, bool support_320, 378362306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 378462306a36Sopenharmony_ci{ 378562306a36Sopenharmony_ci struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional; 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci chandef->center_freq1 = 378862306a36Sopenharmony_ci ieee80211_channel_to_frequency(info->ccfs0, 378962306a36Sopenharmony_ci chandef->chan->band); 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_ci switch (u8_get_bits(info->control, 379262306a36Sopenharmony_ci IEEE80211_EHT_OPER_CHAN_WIDTH)) { 379362306a36Sopenharmony_ci case IEEE80211_EHT_OPER_CHAN_WIDTH_20MHZ: 379462306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_20; 379562306a36Sopenharmony_ci break; 379662306a36Sopenharmony_ci case IEEE80211_EHT_OPER_CHAN_WIDTH_40MHZ: 379762306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_40; 379862306a36Sopenharmony_ci break; 379962306a36Sopenharmony_ci case IEEE80211_EHT_OPER_CHAN_WIDTH_80MHZ: 380062306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_80; 380162306a36Sopenharmony_ci break; 380262306a36Sopenharmony_ci case IEEE80211_EHT_OPER_CHAN_WIDTH_160MHZ: 380362306a36Sopenharmony_ci if (support_160) { 380462306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_160; 380562306a36Sopenharmony_ci chandef->center_freq1 = 380662306a36Sopenharmony_ci ieee80211_channel_to_frequency(info->ccfs1, 380762306a36Sopenharmony_ci chandef->chan->band); 380862306a36Sopenharmony_ci } else { 380962306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_80; 381062306a36Sopenharmony_ci } 381162306a36Sopenharmony_ci break; 381262306a36Sopenharmony_ci case IEEE80211_EHT_OPER_CHAN_WIDTH_320MHZ: 381362306a36Sopenharmony_ci if (support_320) { 381462306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_320; 381562306a36Sopenharmony_ci chandef->center_freq1 = 381662306a36Sopenharmony_ci ieee80211_channel_to_frequency(info->ccfs1, 381762306a36Sopenharmony_ci chandef->chan->band); 381862306a36Sopenharmony_ci } else if (support_160) { 381962306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_160; 382062306a36Sopenharmony_ci } else { 382162306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_80; 382262306a36Sopenharmony_ci 382362306a36Sopenharmony_ci if (chandef->center_freq1 > chandef->chan->center_freq) 382462306a36Sopenharmony_ci chandef->center_freq1 -= 40; 382562306a36Sopenharmony_ci else 382662306a36Sopenharmony_ci chandef->center_freq1 += 40; 382762306a36Sopenharmony_ci } 382862306a36Sopenharmony_ci break; 382962306a36Sopenharmony_ci } 383062306a36Sopenharmony_ci} 383162306a36Sopenharmony_ci 383262306a36Sopenharmony_cibool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, 383362306a36Sopenharmony_ci const struct ieee80211_he_operation *he_oper, 383462306a36Sopenharmony_ci const struct ieee80211_eht_operation *eht_oper, 383562306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 383662306a36Sopenharmony_ci{ 383762306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 383862306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 383962306a36Sopenharmony_ci enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 384062306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 384162306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap; 384262306a36Sopenharmony_ci struct cfg80211_chan_def he_chandef = *chandef; 384362306a36Sopenharmony_ci const struct ieee80211_he_6ghz_oper *he_6ghz_oper; 384462306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; 384562306a36Sopenharmony_ci bool support_80_80, support_160, support_320; 384662306a36Sopenharmony_ci u8 he_phy_cap, eht_phy_cap; 384762306a36Sopenharmony_ci u32 freq; 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_6GHZ) 385062306a36Sopenharmony_ci return true; 385162306a36Sopenharmony_ci 385262306a36Sopenharmony_ci sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 385362306a36Sopenharmony_ci 385462306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 385562306a36Sopenharmony_ci if (!he_cap) { 385662306a36Sopenharmony_ci sdata_info(sdata, "Missing iftype sband data/HE cap"); 385762306a36Sopenharmony_ci return false; 385862306a36Sopenharmony_ci } 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ci he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0]; 386162306a36Sopenharmony_ci support_160 = 386262306a36Sopenharmony_ci he_phy_cap & 386362306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 386462306a36Sopenharmony_ci support_80_80 = 386562306a36Sopenharmony_ci he_phy_cap & 386662306a36Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci if (!he_oper) { 386962306a36Sopenharmony_ci sdata_info(sdata, 387062306a36Sopenharmony_ci "HE is not advertised on (on %d MHz), expect issues\n", 387162306a36Sopenharmony_ci chandef->chan->center_freq); 387262306a36Sopenharmony_ci return false; 387362306a36Sopenharmony_ci } 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); 387662306a36Sopenharmony_ci if (!eht_cap) 387762306a36Sopenharmony_ci eht_oper = NULL; 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_ci if (!he_6ghz_oper) { 388262306a36Sopenharmony_ci sdata_info(sdata, 388362306a36Sopenharmony_ci "HE 6GHz operation missing (on %d MHz), expect issues\n", 388462306a36Sopenharmony_ci chandef->chan->center_freq); 388562306a36Sopenharmony_ci return false; 388662306a36Sopenharmony_ci } 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci /* 388962306a36Sopenharmony_ci * The EHT operation IE does not contain the primary channel so the 389062306a36Sopenharmony_ci * primary channel frequency should be taken from the 6 GHz operation 389162306a36Sopenharmony_ci * information. 389262306a36Sopenharmony_ci */ 389362306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary, 389462306a36Sopenharmony_ci NL80211_BAND_6GHZ); 389562306a36Sopenharmony_ci he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci switch (u8_get_bits(he_6ghz_oper->control, 389862306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_REG_INFO)) { 389962306a36Sopenharmony_ci case IEEE80211_6GHZ_CTRL_REG_LPI_AP: 390062306a36Sopenharmony_ci bss_conf->power_type = IEEE80211_REG_LPI_AP; 390162306a36Sopenharmony_ci break; 390262306a36Sopenharmony_ci case IEEE80211_6GHZ_CTRL_REG_SP_AP: 390362306a36Sopenharmony_ci bss_conf->power_type = IEEE80211_REG_SP_AP; 390462306a36Sopenharmony_ci break; 390562306a36Sopenharmony_ci default: 390662306a36Sopenharmony_ci bss_conf->power_type = IEEE80211_REG_UNSET_AP; 390762306a36Sopenharmony_ci break; 390862306a36Sopenharmony_ci } 390962306a36Sopenharmony_ci 391062306a36Sopenharmony_ci if (!eht_oper || 391162306a36Sopenharmony_ci !(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) { 391262306a36Sopenharmony_ci switch (u8_get_bits(he_6ghz_oper->control, 391362306a36Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { 391462306a36Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ: 391562306a36Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_20; 391662306a36Sopenharmony_ci break; 391762306a36Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ: 391862306a36Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_40; 391962306a36Sopenharmony_ci break; 392062306a36Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ: 392162306a36Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_80; 392262306a36Sopenharmony_ci break; 392362306a36Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ: 392462306a36Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_80; 392562306a36Sopenharmony_ci if (!he_6ghz_oper->ccfs1) 392662306a36Sopenharmony_ci break; 392762306a36Sopenharmony_ci if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) { 392862306a36Sopenharmony_ci if (support_160) 392962306a36Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_160; 393062306a36Sopenharmony_ci } else { 393162306a36Sopenharmony_ci if (support_80_80) 393262306a36Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_80P80; 393362306a36Sopenharmony_ci } 393462306a36Sopenharmony_ci break; 393562306a36Sopenharmony_ci } 393662306a36Sopenharmony_ci 393762306a36Sopenharmony_ci if (he_chandef.width == NL80211_CHAN_WIDTH_160) { 393862306a36Sopenharmony_ci he_chandef.center_freq1 = 393962306a36Sopenharmony_ci ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, 394062306a36Sopenharmony_ci NL80211_BAND_6GHZ); 394162306a36Sopenharmony_ci } else { 394262306a36Sopenharmony_ci he_chandef.center_freq1 = 394362306a36Sopenharmony_ci ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0, 394462306a36Sopenharmony_ci NL80211_BAND_6GHZ); 394562306a36Sopenharmony_ci if (support_80_80 || support_160) 394662306a36Sopenharmony_ci he_chandef.center_freq2 = 394762306a36Sopenharmony_ci ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, 394862306a36Sopenharmony_ci NL80211_BAND_6GHZ); 394962306a36Sopenharmony_ci } 395062306a36Sopenharmony_ci } else { 395162306a36Sopenharmony_ci eht_phy_cap = eht_cap->eht_cap_elem.phy_cap_info[0]; 395262306a36Sopenharmony_ci support_320 = 395362306a36Sopenharmony_ci eht_phy_cap & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_ci ieee80211_chandef_eht_oper(eht_oper, support_160, 395662306a36Sopenharmony_ci support_320, &he_chandef); 395762306a36Sopenharmony_ci } 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci if (!cfg80211_chandef_valid(&he_chandef)) { 396062306a36Sopenharmony_ci sdata_info(sdata, 396162306a36Sopenharmony_ci "HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n", 396262306a36Sopenharmony_ci he_chandef.chan ? he_chandef.chan->center_freq : 0, 396362306a36Sopenharmony_ci he_chandef.width, 396462306a36Sopenharmony_ci he_chandef.center_freq1, 396562306a36Sopenharmony_ci he_chandef.center_freq2); 396662306a36Sopenharmony_ci return false; 396762306a36Sopenharmony_ci } 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci *chandef = he_chandef; 397062306a36Sopenharmony_ci 397162306a36Sopenharmony_ci return true; 397262306a36Sopenharmony_ci} 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_cibool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, 397562306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 397662306a36Sopenharmony_ci{ 397762306a36Sopenharmony_ci u32 oper_freq; 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_ci if (!oper) 398062306a36Sopenharmony_ci return false; 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci switch (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) { 398362306a36Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_1MHZ: 398462306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_1; 398562306a36Sopenharmony_ci break; 398662306a36Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_2MHZ: 398762306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_2; 398862306a36Sopenharmony_ci break; 398962306a36Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_4MHZ: 399062306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_4; 399162306a36Sopenharmony_ci break; 399262306a36Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_8MHZ: 399362306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_8; 399462306a36Sopenharmony_ci break; 399562306a36Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_16MHZ: 399662306a36Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_16; 399762306a36Sopenharmony_ci break; 399862306a36Sopenharmony_ci default: 399962306a36Sopenharmony_ci return false; 400062306a36Sopenharmony_ci } 400162306a36Sopenharmony_ci 400262306a36Sopenharmony_ci oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch, 400362306a36Sopenharmony_ci NL80211_BAND_S1GHZ); 400462306a36Sopenharmony_ci chandef->center_freq1 = KHZ_TO_MHZ(oper_freq); 400562306a36Sopenharmony_ci chandef->freq1_offset = oper_freq % 1000; 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci return true; 400862306a36Sopenharmony_ci} 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ciint ieee80211_parse_bitrates(enum nl80211_chan_width width, 401162306a36Sopenharmony_ci const struct ieee80211_supported_band *sband, 401262306a36Sopenharmony_ci const u8 *srates, int srates_len, u32 *rates) 401362306a36Sopenharmony_ci{ 401462306a36Sopenharmony_ci u32 rate_flags = ieee80211_chanwidth_rate_flags(width); 401562306a36Sopenharmony_ci int shift = ieee80211_chanwidth_get_shift(width); 401662306a36Sopenharmony_ci struct ieee80211_rate *br; 401762306a36Sopenharmony_ci int brate, rate, i, j, count = 0; 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_ci *rates = 0; 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci for (i = 0; i < srates_len; i++) { 402262306a36Sopenharmony_ci rate = srates[i] & 0x7f; 402362306a36Sopenharmony_ci 402462306a36Sopenharmony_ci for (j = 0; j < sband->n_bitrates; j++) { 402562306a36Sopenharmony_ci br = &sband->bitrates[j]; 402662306a36Sopenharmony_ci if ((rate_flags & br->flags) != rate_flags) 402762306a36Sopenharmony_ci continue; 402862306a36Sopenharmony_ci 402962306a36Sopenharmony_ci brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); 403062306a36Sopenharmony_ci if (brate == rate) { 403162306a36Sopenharmony_ci *rates |= BIT(j); 403262306a36Sopenharmony_ci count++; 403362306a36Sopenharmony_ci break; 403462306a36Sopenharmony_ci } 403562306a36Sopenharmony_ci } 403662306a36Sopenharmony_ci } 403762306a36Sopenharmony_ci return count; 403862306a36Sopenharmony_ci} 403962306a36Sopenharmony_ci 404062306a36Sopenharmony_ciint ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, 404162306a36Sopenharmony_ci struct sk_buff *skb, bool need_basic, 404262306a36Sopenharmony_ci enum nl80211_band band) 404362306a36Sopenharmony_ci{ 404462306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 404562306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 404662306a36Sopenharmony_ci int rate, shift; 404762306a36Sopenharmony_ci u8 i, rates, *pos; 404862306a36Sopenharmony_ci u32 basic_rates = sdata->vif.bss_conf.basic_rates; 404962306a36Sopenharmony_ci u32 rate_flags; 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci shift = ieee80211_vif_get_shift(&sdata->vif); 405262306a36Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 405362306a36Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 405462306a36Sopenharmony_ci rates = 0; 405562306a36Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 405662306a36Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 405762306a36Sopenharmony_ci continue; 405862306a36Sopenharmony_ci rates++; 405962306a36Sopenharmony_ci } 406062306a36Sopenharmony_ci if (rates > 8) 406162306a36Sopenharmony_ci rates = 8; 406262306a36Sopenharmony_ci 406362306a36Sopenharmony_ci if (skb_tailroom(skb) < rates + 2) 406462306a36Sopenharmony_ci return -ENOMEM; 406562306a36Sopenharmony_ci 406662306a36Sopenharmony_ci pos = skb_put(skb, rates + 2); 406762306a36Sopenharmony_ci *pos++ = WLAN_EID_SUPP_RATES; 406862306a36Sopenharmony_ci *pos++ = rates; 406962306a36Sopenharmony_ci for (i = 0; i < rates; i++) { 407062306a36Sopenharmony_ci u8 basic = 0; 407162306a36Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 407262306a36Sopenharmony_ci continue; 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci if (need_basic && basic_rates & BIT(i)) 407562306a36Sopenharmony_ci basic = 0x80; 407662306a36Sopenharmony_ci rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 407762306a36Sopenharmony_ci 5 * (1 << shift)); 407862306a36Sopenharmony_ci *pos++ = basic | (u8) rate; 407962306a36Sopenharmony_ci } 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_ci return 0; 408262306a36Sopenharmony_ci} 408362306a36Sopenharmony_ci 408462306a36Sopenharmony_ciint ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, 408562306a36Sopenharmony_ci struct sk_buff *skb, bool need_basic, 408662306a36Sopenharmony_ci enum nl80211_band band) 408762306a36Sopenharmony_ci{ 408862306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 408962306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 409062306a36Sopenharmony_ci int rate, shift; 409162306a36Sopenharmony_ci u8 i, exrates, *pos; 409262306a36Sopenharmony_ci u32 basic_rates = sdata->vif.bss_conf.basic_rates; 409362306a36Sopenharmony_ci u32 rate_flags; 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 409662306a36Sopenharmony_ci shift = ieee80211_vif_get_shift(&sdata->vif); 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 409962306a36Sopenharmony_ci exrates = 0; 410062306a36Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 410162306a36Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 410262306a36Sopenharmony_ci continue; 410362306a36Sopenharmony_ci exrates++; 410462306a36Sopenharmony_ci } 410562306a36Sopenharmony_ci 410662306a36Sopenharmony_ci if (exrates > 8) 410762306a36Sopenharmony_ci exrates -= 8; 410862306a36Sopenharmony_ci else 410962306a36Sopenharmony_ci exrates = 0; 411062306a36Sopenharmony_ci 411162306a36Sopenharmony_ci if (skb_tailroom(skb) < exrates + 2) 411262306a36Sopenharmony_ci return -ENOMEM; 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci if (exrates) { 411562306a36Sopenharmony_ci pos = skb_put(skb, exrates + 2); 411662306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_SUPP_RATES; 411762306a36Sopenharmony_ci *pos++ = exrates; 411862306a36Sopenharmony_ci for (i = 8; i < sband->n_bitrates; i++) { 411962306a36Sopenharmony_ci u8 basic = 0; 412062306a36Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) 412162306a36Sopenharmony_ci != rate_flags) 412262306a36Sopenharmony_ci continue; 412362306a36Sopenharmony_ci if (need_basic && basic_rates & BIT(i)) 412462306a36Sopenharmony_ci basic = 0x80; 412562306a36Sopenharmony_ci rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 412662306a36Sopenharmony_ci 5 * (1 << shift)); 412762306a36Sopenharmony_ci *pos++ = basic | (u8) rate; 412862306a36Sopenharmony_ci } 412962306a36Sopenharmony_ci } 413062306a36Sopenharmony_ci return 0; 413162306a36Sopenharmony_ci} 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_ciint ieee80211_ave_rssi(struct ieee80211_vif *vif) 413462306a36Sopenharmony_ci{ 413562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 413662306a36Sopenharmony_ci 413762306a36Sopenharmony_ci if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) 413862306a36Sopenharmony_ci return 0; 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_ci return -ewma_beacon_signal_read(&sdata->deflink.u.mgd.ave_beacon_signal); 414162306a36Sopenharmony_ci} 414262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_ave_rssi); 414362306a36Sopenharmony_ci 414462306a36Sopenharmony_ciu8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) 414562306a36Sopenharmony_ci{ 414662306a36Sopenharmony_ci if (!mcs) 414762306a36Sopenharmony_ci return 1; 414862306a36Sopenharmony_ci 414962306a36Sopenharmony_ci /* TODO: consider rx_highest */ 415062306a36Sopenharmony_ci 415162306a36Sopenharmony_ci if (mcs->rx_mask[3]) 415262306a36Sopenharmony_ci return 4; 415362306a36Sopenharmony_ci if (mcs->rx_mask[2]) 415462306a36Sopenharmony_ci return 3; 415562306a36Sopenharmony_ci if (mcs->rx_mask[1]) 415662306a36Sopenharmony_ci return 2; 415762306a36Sopenharmony_ci return 1; 415862306a36Sopenharmony_ci} 415962306a36Sopenharmony_ci 416062306a36Sopenharmony_ci/** 416162306a36Sopenharmony_ci * ieee80211_calculate_rx_timestamp - calculate timestamp in frame 416262306a36Sopenharmony_ci * @local: mac80211 hw info struct 416362306a36Sopenharmony_ci * @status: RX status 416462306a36Sopenharmony_ci * @mpdu_len: total MPDU length (including FCS) 416562306a36Sopenharmony_ci * @mpdu_offset: offset into MPDU to calculate timestamp at 416662306a36Sopenharmony_ci * 416762306a36Sopenharmony_ci * This function calculates the RX timestamp at the given MPDU offset, taking 416862306a36Sopenharmony_ci * into account what the RX timestamp was. An offset of 0 will just normalize 416962306a36Sopenharmony_ci * the timestamp to TSF at beginning of MPDU reception. 417062306a36Sopenharmony_ci */ 417162306a36Sopenharmony_ciu64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, 417262306a36Sopenharmony_ci struct ieee80211_rx_status *status, 417362306a36Sopenharmony_ci unsigned int mpdu_len, 417462306a36Sopenharmony_ci unsigned int mpdu_offset) 417562306a36Sopenharmony_ci{ 417662306a36Sopenharmony_ci u64 ts = status->mactime; 417762306a36Sopenharmony_ci struct rate_info ri; 417862306a36Sopenharmony_ci u16 rate; 417962306a36Sopenharmony_ci u8 n_ltf; 418062306a36Sopenharmony_ci 418162306a36Sopenharmony_ci if (WARN_ON(!ieee80211_have_rx_timestamp(status))) 418262306a36Sopenharmony_ci return 0; 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci memset(&ri, 0, sizeof(ri)); 418562306a36Sopenharmony_ci 418662306a36Sopenharmony_ci ri.bw = status->bw; 418762306a36Sopenharmony_ci 418862306a36Sopenharmony_ci /* Fill cfg80211 rate info */ 418962306a36Sopenharmony_ci switch (status->encoding) { 419062306a36Sopenharmony_ci case RX_ENC_EHT: 419162306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_EHT_MCS; 419262306a36Sopenharmony_ci ri.mcs = status->rate_idx; 419362306a36Sopenharmony_ci ri.nss = status->nss; 419462306a36Sopenharmony_ci ri.eht_ru_alloc = status->eht.ru; 419562306a36Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 419662306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 419762306a36Sopenharmony_ci /* TODO/FIXME: is this right? handle other PPDUs */ 419862306a36Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 419962306a36Sopenharmony_ci mpdu_offset += 2; 420062306a36Sopenharmony_ci ts += 36; 420162306a36Sopenharmony_ci } 420262306a36Sopenharmony_ci break; 420362306a36Sopenharmony_ci case RX_ENC_HE: 420462306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_HE_MCS; 420562306a36Sopenharmony_ci ri.mcs = status->rate_idx; 420662306a36Sopenharmony_ci ri.nss = status->nss; 420762306a36Sopenharmony_ci ri.he_ru_alloc = status->he_ru; 420862306a36Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 420962306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 421062306a36Sopenharmony_ci 421162306a36Sopenharmony_ci /* 421262306a36Sopenharmony_ci * See P802.11ax_D6.0, section 27.3.4 for 421362306a36Sopenharmony_ci * VHT PPDU format. 421462306a36Sopenharmony_ci */ 421562306a36Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 421662306a36Sopenharmony_ci mpdu_offset += 2; 421762306a36Sopenharmony_ci ts += 36; 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci /* 422062306a36Sopenharmony_ci * TODO: 422162306a36Sopenharmony_ci * For HE MU PPDU, add the HE-SIG-B. 422262306a36Sopenharmony_ci * For HE ER PPDU, add 8us for the HE-SIG-A. 422362306a36Sopenharmony_ci * For HE TB PPDU, add 4us for the HE-STF. 422462306a36Sopenharmony_ci * Add the HE-LTF durations - variable. 422562306a36Sopenharmony_ci */ 422662306a36Sopenharmony_ci } 422762306a36Sopenharmony_ci 422862306a36Sopenharmony_ci break; 422962306a36Sopenharmony_ci case RX_ENC_HT: 423062306a36Sopenharmony_ci ri.mcs = status->rate_idx; 423162306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_MCS; 423262306a36Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 423362306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_ci /* 423662306a36Sopenharmony_ci * See P802.11REVmd_D3.0, section 19.3.2 for 423762306a36Sopenharmony_ci * HT PPDU format. 423862306a36Sopenharmony_ci */ 423962306a36Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 424062306a36Sopenharmony_ci mpdu_offset += 2; 424162306a36Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_HT_GF) 424262306a36Sopenharmony_ci ts += 24; 424362306a36Sopenharmony_ci else 424462306a36Sopenharmony_ci ts += 32; 424562306a36Sopenharmony_ci 424662306a36Sopenharmony_ci /* 424762306a36Sopenharmony_ci * Add Data HT-LTFs per streams 424862306a36Sopenharmony_ci * TODO: add Extension HT-LTFs, 4us per LTF 424962306a36Sopenharmony_ci */ 425062306a36Sopenharmony_ci n_ltf = ((ri.mcs >> 3) & 3) + 1; 425162306a36Sopenharmony_ci n_ltf = n_ltf == 3 ? 4 : n_ltf; 425262306a36Sopenharmony_ci ts += n_ltf * 4; 425362306a36Sopenharmony_ci } 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_ci break; 425662306a36Sopenharmony_ci case RX_ENC_VHT: 425762306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_VHT_MCS; 425862306a36Sopenharmony_ci ri.mcs = status->rate_idx; 425962306a36Sopenharmony_ci ri.nss = status->nss; 426062306a36Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 426162306a36Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 426262306a36Sopenharmony_ci 426362306a36Sopenharmony_ci /* 426462306a36Sopenharmony_ci * See P802.11REVmd_D3.0, section 21.3.2 for 426562306a36Sopenharmony_ci * VHT PPDU format. 426662306a36Sopenharmony_ci */ 426762306a36Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 426862306a36Sopenharmony_ci mpdu_offset += 2; 426962306a36Sopenharmony_ci ts += 36; 427062306a36Sopenharmony_ci 427162306a36Sopenharmony_ci /* 427262306a36Sopenharmony_ci * Add VHT-LTFs per streams 427362306a36Sopenharmony_ci */ 427462306a36Sopenharmony_ci n_ltf = (ri.nss != 1) && (ri.nss % 2) ? 427562306a36Sopenharmony_ci ri.nss + 1 : ri.nss; 427662306a36Sopenharmony_ci ts += 4 * n_ltf; 427762306a36Sopenharmony_ci } 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci break; 428062306a36Sopenharmony_ci default: 428162306a36Sopenharmony_ci WARN_ON(1); 428262306a36Sopenharmony_ci fallthrough; 428362306a36Sopenharmony_ci case RX_ENC_LEGACY: { 428462306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 428562306a36Sopenharmony_ci int shift = 0; 428662306a36Sopenharmony_ci int bitrate; 428762306a36Sopenharmony_ci 428862306a36Sopenharmony_ci switch (status->bw) { 428962306a36Sopenharmony_ci case RATE_INFO_BW_10: 429062306a36Sopenharmony_ci shift = 1; 429162306a36Sopenharmony_ci break; 429262306a36Sopenharmony_ci case RATE_INFO_BW_5: 429362306a36Sopenharmony_ci shift = 2; 429462306a36Sopenharmony_ci break; 429562306a36Sopenharmony_ci } 429662306a36Sopenharmony_ci 429762306a36Sopenharmony_ci sband = local->hw.wiphy->bands[status->band]; 429862306a36Sopenharmony_ci bitrate = sband->bitrates[status->rate_idx].bitrate; 429962306a36Sopenharmony_ci ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 430262306a36Sopenharmony_ci if (status->band == NL80211_BAND_5GHZ) { 430362306a36Sopenharmony_ci ts += 20 << shift; 430462306a36Sopenharmony_ci mpdu_offset += 2; 430562306a36Sopenharmony_ci } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) { 430662306a36Sopenharmony_ci ts += 96; 430762306a36Sopenharmony_ci } else { 430862306a36Sopenharmony_ci ts += 192; 430962306a36Sopenharmony_ci } 431062306a36Sopenharmony_ci } 431162306a36Sopenharmony_ci break; 431262306a36Sopenharmony_ci } 431362306a36Sopenharmony_ci } 431462306a36Sopenharmony_ci 431562306a36Sopenharmony_ci rate = cfg80211_calculate_bitrate(&ri); 431662306a36Sopenharmony_ci if (WARN_ONCE(!rate, 431762306a36Sopenharmony_ci "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n", 431862306a36Sopenharmony_ci (unsigned long long)status->flag, status->rate_idx, 431962306a36Sopenharmony_ci status->nss)) 432062306a36Sopenharmony_ci return 0; 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci /* rewind from end of MPDU */ 432362306a36Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_END) 432462306a36Sopenharmony_ci ts -= mpdu_len * 8 * 10 / rate; 432562306a36Sopenharmony_ci 432662306a36Sopenharmony_ci ts += mpdu_offset * 8 * 10 / rate; 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_ci return ts; 432962306a36Sopenharmony_ci} 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_civoid ieee80211_dfs_cac_cancel(struct ieee80211_local *local) 433262306a36Sopenharmony_ci{ 433362306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 433462306a36Sopenharmony_ci struct cfg80211_chan_def chandef; 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */ 433762306a36Sopenharmony_ci lockdep_assert_wiphy(local->hw.wiphy); 433862306a36Sopenharmony_ci 433962306a36Sopenharmony_ci mutex_lock(&local->mtx); 434062306a36Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 434162306a36Sopenharmony_ci /* it might be waiting for the local->mtx, but then 434262306a36Sopenharmony_ci * by the time it gets it, sdata->wdev.cac_started 434362306a36Sopenharmony_ci * will no longer be true 434462306a36Sopenharmony_ci */ 434562306a36Sopenharmony_ci cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work); 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci if (sdata->wdev.cac_started) { 434862306a36Sopenharmony_ci chandef = sdata->vif.bss_conf.chandef; 434962306a36Sopenharmony_ci ieee80211_link_release_channel(&sdata->deflink); 435062306a36Sopenharmony_ci cfg80211_cac_event(sdata->dev, 435162306a36Sopenharmony_ci &chandef, 435262306a36Sopenharmony_ci NL80211_RADAR_CAC_ABORTED, 435362306a36Sopenharmony_ci GFP_KERNEL); 435462306a36Sopenharmony_ci } 435562306a36Sopenharmony_ci } 435662306a36Sopenharmony_ci mutex_unlock(&local->mtx); 435762306a36Sopenharmony_ci} 435862306a36Sopenharmony_ci 435962306a36Sopenharmony_civoid ieee80211_dfs_radar_detected_work(struct wiphy *wiphy, 436062306a36Sopenharmony_ci struct wiphy_work *work) 436162306a36Sopenharmony_ci{ 436262306a36Sopenharmony_ci struct ieee80211_local *local = 436362306a36Sopenharmony_ci container_of(work, struct ieee80211_local, radar_detected_work); 436462306a36Sopenharmony_ci struct cfg80211_chan_def chandef = local->hw.conf.chandef; 436562306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 436662306a36Sopenharmony_ci int num_chanctx = 0; 436762306a36Sopenharmony_ci 436862306a36Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 436962306a36Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) { 437062306a36Sopenharmony_ci if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) 437162306a36Sopenharmony_ci continue; 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci num_chanctx++; 437462306a36Sopenharmony_ci chandef = ctx->conf.def; 437562306a36Sopenharmony_ci } 437662306a36Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 437762306a36Sopenharmony_ci 437862306a36Sopenharmony_ci ieee80211_dfs_cac_cancel(local); 437962306a36Sopenharmony_ci 438062306a36Sopenharmony_ci if (num_chanctx > 1) 438162306a36Sopenharmony_ci /* XXX: multi-channel is not supported yet */ 438262306a36Sopenharmony_ci WARN_ON(1); 438362306a36Sopenharmony_ci else 438462306a36Sopenharmony_ci cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); 438562306a36Sopenharmony_ci} 438662306a36Sopenharmony_ci 438762306a36Sopenharmony_civoid ieee80211_radar_detected(struct ieee80211_hw *hw) 438862306a36Sopenharmony_ci{ 438962306a36Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_ci trace_api_radar_detected(local); 439262306a36Sopenharmony_ci 439362306a36Sopenharmony_ci wiphy_work_queue(hw->wiphy, &local->radar_detected_work); 439462306a36Sopenharmony_ci} 439562306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_radar_detected); 439662306a36Sopenharmony_ci 439762306a36Sopenharmony_ciieee80211_conn_flags_t ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) 439862306a36Sopenharmony_ci{ 439962306a36Sopenharmony_ci ieee80211_conn_flags_t ret; 440062306a36Sopenharmony_ci int tmp; 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_ci switch (c->width) { 440362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 440462306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_20_NOHT; 440562306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT; 440662306a36Sopenharmony_ci break; 440762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 440862306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_20; 440962306a36Sopenharmony_ci c->center_freq1 = c->chan->center_freq; 441062306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_40MHZ | 441162306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_VHT; 441262306a36Sopenharmony_ci break; 441362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 441462306a36Sopenharmony_ci tmp = (30 + c->chan->center_freq - c->center_freq1)/20; 441562306a36Sopenharmony_ci /* n_P40 */ 441662306a36Sopenharmony_ci tmp /= 2; 441762306a36Sopenharmony_ci /* freq_P40 */ 441862306a36Sopenharmony_ci c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; 441962306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_40; 442062306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_VHT; 442162306a36Sopenharmony_ci break; 442262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 442362306a36Sopenharmony_ci c->center_freq2 = 0; 442462306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_80; 442562306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_80P80MHZ | 442662306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_160MHZ; 442762306a36Sopenharmony_ci break; 442862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 442962306a36Sopenharmony_ci /* n_P20 */ 443062306a36Sopenharmony_ci tmp = (70 + c->chan->center_freq - c->center_freq1)/20; 443162306a36Sopenharmony_ci /* n_P80 */ 443262306a36Sopenharmony_ci tmp /= 4; 443362306a36Sopenharmony_ci c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; 443462306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_80; 443562306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_80P80MHZ | 443662306a36Sopenharmony_ci IEEE80211_CONN_DISABLE_160MHZ; 443762306a36Sopenharmony_ci break; 443862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_320: 443962306a36Sopenharmony_ci /* n_P20 */ 444062306a36Sopenharmony_ci tmp = (150 + c->chan->center_freq - c->center_freq1) / 20; 444162306a36Sopenharmony_ci /* n_P160 */ 444262306a36Sopenharmony_ci tmp /= 8; 444362306a36Sopenharmony_ci c->center_freq1 = c->center_freq1 - 80 + 160 * tmp; 444462306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_160; 444562306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_320MHZ; 444662306a36Sopenharmony_ci break; 444762306a36Sopenharmony_ci default: 444862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 444962306a36Sopenharmony_ci WARN_ON_ONCE(1); 445062306a36Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_20_NOHT; 445162306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT; 445262306a36Sopenharmony_ci break; 445362306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_1: 445462306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_2: 445562306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_4: 445662306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_8: 445762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_16: 445862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 445962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 446062306a36Sopenharmony_ci WARN_ON_ONCE(1); 446162306a36Sopenharmony_ci /* keep c->width */ 446262306a36Sopenharmony_ci ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_VHT; 446362306a36Sopenharmony_ci break; 446462306a36Sopenharmony_ci } 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci WARN_ON_ONCE(!cfg80211_chandef_valid(c)); 446762306a36Sopenharmony_ci 446862306a36Sopenharmony_ci return ret; 446962306a36Sopenharmony_ci} 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_ci/* 447262306a36Sopenharmony_ci * Returns true if smps_mode_new is strictly more restrictive than 447362306a36Sopenharmony_ci * smps_mode_old. 447462306a36Sopenharmony_ci */ 447562306a36Sopenharmony_cibool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, 447662306a36Sopenharmony_ci enum ieee80211_smps_mode smps_mode_new) 447762306a36Sopenharmony_ci{ 447862306a36Sopenharmony_ci if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || 447962306a36Sopenharmony_ci smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) 448062306a36Sopenharmony_ci return false; 448162306a36Sopenharmony_ci 448262306a36Sopenharmony_ci switch (smps_mode_old) { 448362306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 448462306a36Sopenharmony_ci return false; 448562306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 448662306a36Sopenharmony_ci return smps_mode_new == IEEE80211_SMPS_STATIC; 448762306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 448862306a36Sopenharmony_ci return smps_mode_new != IEEE80211_SMPS_OFF; 448962306a36Sopenharmony_ci default: 449062306a36Sopenharmony_ci WARN_ON(1); 449162306a36Sopenharmony_ci } 449262306a36Sopenharmony_ci 449362306a36Sopenharmony_ci return false; 449462306a36Sopenharmony_ci} 449562306a36Sopenharmony_ci 449662306a36Sopenharmony_ciint ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, 449762306a36Sopenharmony_ci struct cfg80211_csa_settings *csa_settings) 449862306a36Sopenharmony_ci{ 449962306a36Sopenharmony_ci struct sk_buff *skb; 450062306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt; 450162306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 450262306a36Sopenharmony_ci int freq; 450362306a36Sopenharmony_ci int hdr_len = offsetofend(struct ieee80211_mgmt, 450462306a36Sopenharmony_ci u.action.u.chan_switch); 450562306a36Sopenharmony_ci u8 *pos; 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_ADHOC && 450862306a36Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_MESH_POINT) 450962306a36Sopenharmony_ci return -EOPNOTSUPP; 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci skb = dev_alloc_skb(local->tx_headroom + hdr_len + 451262306a36Sopenharmony_ci 5 + /* channel switch announcement element */ 451362306a36Sopenharmony_ci 3 + /* secondary channel offset element */ 451462306a36Sopenharmony_ci 5 + /* wide bandwidth channel switch announcement */ 451562306a36Sopenharmony_ci 8); /* mesh channel switch parameters element */ 451662306a36Sopenharmony_ci if (!skb) 451762306a36Sopenharmony_ci return -ENOMEM; 451862306a36Sopenharmony_ci 451962306a36Sopenharmony_ci skb_reserve(skb, local->tx_headroom); 452062306a36Sopenharmony_ci mgmt = skb_put_zero(skb, hdr_len); 452162306a36Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 452262306a36Sopenharmony_ci IEEE80211_STYPE_ACTION); 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_ci eth_broadcast_addr(mgmt->da); 452562306a36Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 452662306a36Sopenharmony_ci if (ieee80211_vif_is_mesh(&sdata->vif)) { 452762306a36Sopenharmony_ci memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 452862306a36Sopenharmony_ci } else { 452962306a36Sopenharmony_ci struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 453062306a36Sopenharmony_ci memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); 453162306a36Sopenharmony_ci } 453262306a36Sopenharmony_ci mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 453362306a36Sopenharmony_ci mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; 453462306a36Sopenharmony_ci pos = skb_put(skb, 5); 453562306a36Sopenharmony_ci *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ 453662306a36Sopenharmony_ci *pos++ = 3; /* IE length */ 453762306a36Sopenharmony_ci *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ 453862306a36Sopenharmony_ci freq = csa_settings->chandef.chan->center_freq; 453962306a36Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ 454062306a36Sopenharmony_ci *pos++ = csa_settings->count; /* count */ 454162306a36Sopenharmony_ci 454262306a36Sopenharmony_ci if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { 454362306a36Sopenharmony_ci enum nl80211_channel_type ch_type; 454462306a36Sopenharmony_ci 454562306a36Sopenharmony_ci skb_put(skb, 3); 454662306a36Sopenharmony_ci *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ 454762306a36Sopenharmony_ci *pos++ = 1; /* IE length */ 454862306a36Sopenharmony_ci ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); 454962306a36Sopenharmony_ci if (ch_type == NL80211_CHAN_HT40PLUS) 455062306a36Sopenharmony_ci *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 455162306a36Sopenharmony_ci else 455262306a36Sopenharmony_ci *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 455362306a36Sopenharmony_ci } 455462306a36Sopenharmony_ci 455562306a36Sopenharmony_ci if (ieee80211_vif_is_mesh(&sdata->vif)) { 455662306a36Sopenharmony_ci struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 455762306a36Sopenharmony_ci 455862306a36Sopenharmony_ci skb_put(skb, 8); 455962306a36Sopenharmony_ci *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ 456062306a36Sopenharmony_ci *pos++ = 6; /* IE length */ 456162306a36Sopenharmony_ci *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ 456262306a36Sopenharmony_ci *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ 456362306a36Sopenharmony_ci *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; 456462306a36Sopenharmony_ci *pos++ |= csa_settings->block_tx ? 456562306a36Sopenharmony_ci WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; 456662306a36Sopenharmony_ci put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ 456762306a36Sopenharmony_ci pos += 2; 456862306a36Sopenharmony_ci put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ 456962306a36Sopenharmony_ci pos += 2; 457062306a36Sopenharmony_ci } 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_80 || 457362306a36Sopenharmony_ci csa_settings->chandef.width == NL80211_CHAN_WIDTH_80P80 || 457462306a36Sopenharmony_ci csa_settings->chandef.width == NL80211_CHAN_WIDTH_160) { 457562306a36Sopenharmony_ci skb_put(skb, 5); 457662306a36Sopenharmony_ci ieee80211_ie_build_wide_bw_cs(pos, &csa_settings->chandef); 457762306a36Sopenharmony_ci } 457862306a36Sopenharmony_ci 457962306a36Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 458062306a36Sopenharmony_ci return 0; 458162306a36Sopenharmony_ci} 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_cistatic bool 458462306a36Sopenharmony_ciieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) 458562306a36Sopenharmony_ci{ 458662306a36Sopenharmony_ci s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); 458762306a36Sopenharmony_ci int skip; 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_ci if (end > 0) 459062306a36Sopenharmony_ci return false; 459162306a36Sopenharmony_ci 459262306a36Sopenharmony_ci /* One shot NOA */ 459362306a36Sopenharmony_ci if (data->count[i] == 1) 459462306a36Sopenharmony_ci return false; 459562306a36Sopenharmony_ci 459662306a36Sopenharmony_ci if (data->desc[i].interval == 0) 459762306a36Sopenharmony_ci return false; 459862306a36Sopenharmony_ci 459962306a36Sopenharmony_ci /* End time is in the past, check for repetitions */ 460062306a36Sopenharmony_ci skip = DIV_ROUND_UP(-end, data->desc[i].interval); 460162306a36Sopenharmony_ci if (data->count[i] < 255) { 460262306a36Sopenharmony_ci if (data->count[i] <= skip) { 460362306a36Sopenharmony_ci data->count[i] = 0; 460462306a36Sopenharmony_ci return false; 460562306a36Sopenharmony_ci } 460662306a36Sopenharmony_ci 460762306a36Sopenharmony_ci data->count[i] -= skip; 460862306a36Sopenharmony_ci } 460962306a36Sopenharmony_ci 461062306a36Sopenharmony_ci data->desc[i].start += skip * data->desc[i].interval; 461162306a36Sopenharmony_ci 461262306a36Sopenharmony_ci return true; 461362306a36Sopenharmony_ci} 461462306a36Sopenharmony_ci 461562306a36Sopenharmony_cistatic bool 461662306a36Sopenharmony_ciieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, 461762306a36Sopenharmony_ci s32 *offset) 461862306a36Sopenharmony_ci{ 461962306a36Sopenharmony_ci bool ret = false; 462062306a36Sopenharmony_ci int i; 462162306a36Sopenharmony_ci 462262306a36Sopenharmony_ci for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 462362306a36Sopenharmony_ci s32 cur; 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci if (!data->count[i]) 462662306a36Sopenharmony_ci continue; 462762306a36Sopenharmony_ci 462862306a36Sopenharmony_ci if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) 462962306a36Sopenharmony_ci ret = true; 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci cur = data->desc[i].start - tsf; 463262306a36Sopenharmony_ci if (cur > *offset) 463362306a36Sopenharmony_ci continue; 463462306a36Sopenharmony_ci 463562306a36Sopenharmony_ci cur = data->desc[i].start + data->desc[i].duration - tsf; 463662306a36Sopenharmony_ci if (cur > *offset) 463762306a36Sopenharmony_ci *offset = cur; 463862306a36Sopenharmony_ci } 463962306a36Sopenharmony_ci 464062306a36Sopenharmony_ci return ret; 464162306a36Sopenharmony_ci} 464262306a36Sopenharmony_ci 464362306a36Sopenharmony_cistatic u32 464462306a36Sopenharmony_ciieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) 464562306a36Sopenharmony_ci{ 464662306a36Sopenharmony_ci s32 offset = 0; 464762306a36Sopenharmony_ci int tries = 0; 464862306a36Sopenharmony_ci /* 464962306a36Sopenharmony_ci * arbitrary limit, used to avoid infinite loops when combined NoA 465062306a36Sopenharmony_ci * descriptors cover the full time period. 465162306a36Sopenharmony_ci */ 465262306a36Sopenharmony_ci int max_tries = 5; 465362306a36Sopenharmony_ci 465462306a36Sopenharmony_ci ieee80211_extend_absent_time(data, tsf, &offset); 465562306a36Sopenharmony_ci do { 465662306a36Sopenharmony_ci if (!ieee80211_extend_absent_time(data, tsf, &offset)) 465762306a36Sopenharmony_ci break; 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_ci tries++; 466062306a36Sopenharmony_ci } while (tries < max_tries); 466162306a36Sopenharmony_ci 466262306a36Sopenharmony_ci return offset; 466362306a36Sopenharmony_ci} 466462306a36Sopenharmony_ci 466562306a36Sopenharmony_civoid ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) 466662306a36Sopenharmony_ci{ 466762306a36Sopenharmony_ci u32 next_offset = BIT(31) - 1; 466862306a36Sopenharmony_ci int i; 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci data->absent = 0; 467162306a36Sopenharmony_ci data->has_next_tsf = false; 467262306a36Sopenharmony_ci for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 467362306a36Sopenharmony_ci s32 start; 467462306a36Sopenharmony_ci 467562306a36Sopenharmony_ci if (!data->count[i]) 467662306a36Sopenharmony_ci continue; 467762306a36Sopenharmony_ci 467862306a36Sopenharmony_ci ieee80211_extend_noa_desc(data, tsf, i); 467962306a36Sopenharmony_ci start = data->desc[i].start - tsf; 468062306a36Sopenharmony_ci if (start <= 0) 468162306a36Sopenharmony_ci data->absent |= BIT(i); 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci if (next_offset > start) 468462306a36Sopenharmony_ci next_offset = start; 468562306a36Sopenharmony_ci 468662306a36Sopenharmony_ci data->has_next_tsf = true; 468762306a36Sopenharmony_ci } 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci if (data->absent) 469062306a36Sopenharmony_ci next_offset = ieee80211_get_noa_absent_time(data, tsf); 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_ci data->next_tsf = tsf + next_offset; 469362306a36Sopenharmony_ci} 469462306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_update_p2p_noa); 469562306a36Sopenharmony_ci 469662306a36Sopenharmony_ciint ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, 469762306a36Sopenharmony_ci struct ieee80211_noa_data *data, u32 tsf) 469862306a36Sopenharmony_ci{ 469962306a36Sopenharmony_ci int ret = 0; 470062306a36Sopenharmony_ci int i; 470162306a36Sopenharmony_ci 470262306a36Sopenharmony_ci memset(data, 0, sizeof(*data)); 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_ci for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 470562306a36Sopenharmony_ci const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; 470662306a36Sopenharmony_ci 470762306a36Sopenharmony_ci if (!desc->count || !desc->duration) 470862306a36Sopenharmony_ci continue; 470962306a36Sopenharmony_ci 471062306a36Sopenharmony_ci data->count[i] = desc->count; 471162306a36Sopenharmony_ci data->desc[i].start = le32_to_cpu(desc->start_time); 471262306a36Sopenharmony_ci data->desc[i].duration = le32_to_cpu(desc->duration); 471362306a36Sopenharmony_ci data->desc[i].interval = le32_to_cpu(desc->interval); 471462306a36Sopenharmony_ci 471562306a36Sopenharmony_ci if (data->count[i] > 1 && 471662306a36Sopenharmony_ci data->desc[i].interval < data->desc[i].duration) 471762306a36Sopenharmony_ci continue; 471862306a36Sopenharmony_ci 471962306a36Sopenharmony_ci ieee80211_extend_noa_desc(data, tsf, i); 472062306a36Sopenharmony_ci ret++; 472162306a36Sopenharmony_ci } 472262306a36Sopenharmony_ci 472362306a36Sopenharmony_ci if (ret) 472462306a36Sopenharmony_ci ieee80211_update_p2p_noa(data, tsf); 472562306a36Sopenharmony_ci 472662306a36Sopenharmony_ci return ret; 472762306a36Sopenharmony_ci} 472862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_parse_p2p_noa); 472962306a36Sopenharmony_ci 473062306a36Sopenharmony_civoid ieee80211_recalc_dtim(struct ieee80211_local *local, 473162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 473262306a36Sopenharmony_ci{ 473362306a36Sopenharmony_ci u64 tsf = drv_get_tsf(local, sdata); 473462306a36Sopenharmony_ci u64 dtim_count = 0; 473562306a36Sopenharmony_ci u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; 473662306a36Sopenharmony_ci u8 dtim_period = sdata->vif.bss_conf.dtim_period; 473762306a36Sopenharmony_ci struct ps_data *ps; 473862306a36Sopenharmony_ci u8 bcns_from_dtim; 473962306a36Sopenharmony_ci 474062306a36Sopenharmony_ci if (tsf == -1ULL || !beacon_int || !dtim_period) 474162306a36Sopenharmony_ci return; 474262306a36Sopenharmony_ci 474362306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP || 474462306a36Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 474562306a36Sopenharmony_ci if (!sdata->bss) 474662306a36Sopenharmony_ci return; 474762306a36Sopenharmony_ci 474862306a36Sopenharmony_ci ps = &sdata->bss->ps; 474962306a36Sopenharmony_ci } else if (ieee80211_vif_is_mesh(&sdata->vif)) { 475062306a36Sopenharmony_ci ps = &sdata->u.mesh.ps; 475162306a36Sopenharmony_ci } else { 475262306a36Sopenharmony_ci return; 475362306a36Sopenharmony_ci } 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_ci /* 475662306a36Sopenharmony_ci * actually finds last dtim_count, mac80211 will update in 475762306a36Sopenharmony_ci * __beacon_add_tim(). 475862306a36Sopenharmony_ci * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period 475962306a36Sopenharmony_ci */ 476062306a36Sopenharmony_ci do_div(tsf, beacon_int); 476162306a36Sopenharmony_ci bcns_from_dtim = do_div(tsf, dtim_period); 476262306a36Sopenharmony_ci /* just had a DTIM */ 476362306a36Sopenharmony_ci if (!bcns_from_dtim) 476462306a36Sopenharmony_ci dtim_count = 0; 476562306a36Sopenharmony_ci else 476662306a36Sopenharmony_ci dtim_count = dtim_period - bcns_from_dtim; 476762306a36Sopenharmony_ci 476862306a36Sopenharmony_ci ps->dtim_count = dtim_count; 476962306a36Sopenharmony_ci} 477062306a36Sopenharmony_ci 477162306a36Sopenharmony_cistatic u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, 477262306a36Sopenharmony_ci struct ieee80211_chanctx *ctx) 477362306a36Sopenharmony_ci{ 477462306a36Sopenharmony_ci struct ieee80211_link_data *link; 477562306a36Sopenharmony_ci u8 radar_detect = 0; 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci lockdep_assert_held(&local->chanctx_mtx); 477862306a36Sopenharmony_ci 477962306a36Sopenharmony_ci if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)) 478062306a36Sopenharmony_ci return 0; 478162306a36Sopenharmony_ci 478262306a36Sopenharmony_ci list_for_each_entry(link, &ctx->reserved_links, reserved_chanctx_list) 478362306a36Sopenharmony_ci if (link->reserved_radar_required) 478462306a36Sopenharmony_ci radar_detect |= BIT(link->reserved_chandef.width); 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_ci /* 478762306a36Sopenharmony_ci * An in-place reservation context should not have any assigned vifs 478862306a36Sopenharmony_ci * until it replaces the other context. 478962306a36Sopenharmony_ci */ 479062306a36Sopenharmony_ci WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER && 479162306a36Sopenharmony_ci !list_empty(&ctx->assigned_links)); 479262306a36Sopenharmony_ci 479362306a36Sopenharmony_ci list_for_each_entry(link, &ctx->assigned_links, assigned_chanctx_list) { 479462306a36Sopenharmony_ci if (!link->radar_required) 479562306a36Sopenharmony_ci continue; 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci radar_detect |= 479862306a36Sopenharmony_ci BIT(link->conf->chandef.width); 479962306a36Sopenharmony_ci } 480062306a36Sopenharmony_ci 480162306a36Sopenharmony_ci return radar_detect; 480262306a36Sopenharmony_ci} 480362306a36Sopenharmony_ci 480462306a36Sopenharmony_ciint ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, 480562306a36Sopenharmony_ci const struct cfg80211_chan_def *chandef, 480662306a36Sopenharmony_ci enum ieee80211_chanctx_mode chanmode, 480762306a36Sopenharmony_ci u8 radar_detect) 480862306a36Sopenharmony_ci{ 480962306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 481062306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata_iter; 481162306a36Sopenharmony_ci enum nl80211_iftype iftype = sdata->wdev.iftype; 481262306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 481362306a36Sopenharmony_ci int total = 1; 481462306a36Sopenharmony_ci struct iface_combination_params params = { 481562306a36Sopenharmony_ci .radar_detect = radar_detect, 481662306a36Sopenharmony_ci }; 481762306a36Sopenharmony_ci 481862306a36Sopenharmony_ci lockdep_assert_held(&local->chanctx_mtx); 481962306a36Sopenharmony_ci 482062306a36Sopenharmony_ci if (WARN_ON(hweight32(radar_detect) > 1)) 482162306a36Sopenharmony_ci return -EINVAL; 482262306a36Sopenharmony_ci 482362306a36Sopenharmony_ci if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED && 482462306a36Sopenharmony_ci !chandef->chan)) 482562306a36Sopenharmony_ci return -EINVAL; 482662306a36Sopenharmony_ci 482762306a36Sopenharmony_ci if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) 482862306a36Sopenharmony_ci return -EINVAL; 482962306a36Sopenharmony_ci 483062306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP || 483162306a36Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_MESH_POINT) { 483262306a36Sopenharmony_ci /* 483362306a36Sopenharmony_ci * always passing this is harmless, since it'll be the 483462306a36Sopenharmony_ci * same value that cfg80211 finds if it finds the same 483562306a36Sopenharmony_ci * interface ... and that's always allowed 483662306a36Sopenharmony_ci */ 483762306a36Sopenharmony_ci params.new_beacon_int = sdata->vif.bss_conf.beacon_int; 483862306a36Sopenharmony_ci } 483962306a36Sopenharmony_ci 484062306a36Sopenharmony_ci /* Always allow software iftypes */ 484162306a36Sopenharmony_ci if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) { 484262306a36Sopenharmony_ci if (radar_detect) 484362306a36Sopenharmony_ci return -EINVAL; 484462306a36Sopenharmony_ci return 0; 484562306a36Sopenharmony_ci } 484662306a36Sopenharmony_ci 484762306a36Sopenharmony_ci if (chandef) 484862306a36Sopenharmony_ci params.num_different_channels = 1; 484962306a36Sopenharmony_ci 485062306a36Sopenharmony_ci if (iftype != NL80211_IFTYPE_UNSPECIFIED) 485162306a36Sopenharmony_ci params.iftype_num[iftype] = 1; 485262306a36Sopenharmony_ci 485362306a36Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) { 485462306a36Sopenharmony_ci if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 485562306a36Sopenharmony_ci continue; 485662306a36Sopenharmony_ci params.radar_detect |= 485762306a36Sopenharmony_ci ieee80211_chanctx_radar_detect(local, ctx); 485862306a36Sopenharmony_ci if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { 485962306a36Sopenharmony_ci params.num_different_channels++; 486062306a36Sopenharmony_ci continue; 486162306a36Sopenharmony_ci } 486262306a36Sopenharmony_ci if (chandef && chanmode == IEEE80211_CHANCTX_SHARED && 486362306a36Sopenharmony_ci cfg80211_chandef_compatible(chandef, 486462306a36Sopenharmony_ci &ctx->conf.def)) 486562306a36Sopenharmony_ci continue; 486662306a36Sopenharmony_ci params.num_different_channels++; 486762306a36Sopenharmony_ci } 486862306a36Sopenharmony_ci 486962306a36Sopenharmony_ci list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { 487062306a36Sopenharmony_ci struct wireless_dev *wdev_iter; 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci wdev_iter = &sdata_iter->wdev; 487362306a36Sopenharmony_ci 487462306a36Sopenharmony_ci if (sdata_iter == sdata || 487562306a36Sopenharmony_ci !ieee80211_sdata_running(sdata_iter) || 487662306a36Sopenharmony_ci cfg80211_iftype_allowed(local->hw.wiphy, 487762306a36Sopenharmony_ci wdev_iter->iftype, 0, 1)) 487862306a36Sopenharmony_ci continue; 487962306a36Sopenharmony_ci 488062306a36Sopenharmony_ci params.iftype_num[wdev_iter->iftype]++; 488162306a36Sopenharmony_ci total++; 488262306a36Sopenharmony_ci } 488362306a36Sopenharmony_ci 488462306a36Sopenharmony_ci if (total == 1 && !params.radar_detect) 488562306a36Sopenharmony_ci return 0; 488662306a36Sopenharmony_ci 488762306a36Sopenharmony_ci return cfg80211_check_combinations(local->hw.wiphy, ¶ms); 488862306a36Sopenharmony_ci} 488962306a36Sopenharmony_ci 489062306a36Sopenharmony_cistatic void 489162306a36Sopenharmony_ciieee80211_iter_max_chans(const struct ieee80211_iface_combination *c, 489262306a36Sopenharmony_ci void *data) 489362306a36Sopenharmony_ci{ 489462306a36Sopenharmony_ci u32 *max_num_different_channels = data; 489562306a36Sopenharmony_ci 489662306a36Sopenharmony_ci *max_num_different_channels = max(*max_num_different_channels, 489762306a36Sopenharmony_ci c->num_different_channels); 489862306a36Sopenharmony_ci} 489962306a36Sopenharmony_ci 490062306a36Sopenharmony_ciint ieee80211_max_num_channels(struct ieee80211_local *local) 490162306a36Sopenharmony_ci{ 490262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 490362306a36Sopenharmony_ci struct ieee80211_chanctx *ctx; 490462306a36Sopenharmony_ci u32 max_num_different_channels = 1; 490562306a36Sopenharmony_ci int err; 490662306a36Sopenharmony_ci struct iface_combination_params params = {0}; 490762306a36Sopenharmony_ci 490862306a36Sopenharmony_ci lockdep_assert_held(&local->chanctx_mtx); 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) { 491162306a36Sopenharmony_ci if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 491262306a36Sopenharmony_ci continue; 491362306a36Sopenharmony_ci 491462306a36Sopenharmony_ci params.num_different_channels++; 491562306a36Sopenharmony_ci 491662306a36Sopenharmony_ci params.radar_detect |= 491762306a36Sopenharmony_ci ieee80211_chanctx_radar_detect(local, ctx); 491862306a36Sopenharmony_ci } 491962306a36Sopenharmony_ci 492062306a36Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) 492162306a36Sopenharmony_ci params.iftype_num[sdata->wdev.iftype]++; 492262306a36Sopenharmony_ci 492362306a36Sopenharmony_ci err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms, 492462306a36Sopenharmony_ci ieee80211_iter_max_chans, 492562306a36Sopenharmony_ci &max_num_different_channels); 492662306a36Sopenharmony_ci if (err < 0) 492762306a36Sopenharmony_ci return err; 492862306a36Sopenharmony_ci 492962306a36Sopenharmony_ci return max_num_different_channels; 493062306a36Sopenharmony_ci} 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_civoid ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata, 493362306a36Sopenharmony_ci struct ieee80211_sta_s1g_cap *caps, 493462306a36Sopenharmony_ci struct sk_buff *skb) 493562306a36Sopenharmony_ci{ 493662306a36Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 493762306a36Sopenharmony_ci struct ieee80211_s1g_cap s1g_capab; 493862306a36Sopenharmony_ci u8 *pos; 493962306a36Sopenharmony_ci int i; 494062306a36Sopenharmony_ci 494162306a36Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 494262306a36Sopenharmony_ci return; 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci if (!caps->s1g) 494562306a36Sopenharmony_ci return; 494662306a36Sopenharmony_ci 494762306a36Sopenharmony_ci memcpy(s1g_capab.capab_info, caps->cap, sizeof(caps->cap)); 494862306a36Sopenharmony_ci memcpy(s1g_capab.supp_mcs_nss, caps->nss_mcs, sizeof(caps->nss_mcs)); 494962306a36Sopenharmony_ci 495062306a36Sopenharmony_ci /* override the capability info */ 495162306a36Sopenharmony_ci for (i = 0; i < sizeof(ifmgd->s1g_capa.capab_info); i++) { 495262306a36Sopenharmony_ci u8 mask = ifmgd->s1g_capa_mask.capab_info[i]; 495362306a36Sopenharmony_ci 495462306a36Sopenharmony_ci s1g_capab.capab_info[i] &= ~mask; 495562306a36Sopenharmony_ci s1g_capab.capab_info[i] |= ifmgd->s1g_capa.capab_info[i] & mask; 495662306a36Sopenharmony_ci } 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci /* then MCS and NSS set */ 495962306a36Sopenharmony_ci for (i = 0; i < sizeof(ifmgd->s1g_capa.supp_mcs_nss); i++) { 496062306a36Sopenharmony_ci u8 mask = ifmgd->s1g_capa_mask.supp_mcs_nss[i]; 496162306a36Sopenharmony_ci 496262306a36Sopenharmony_ci s1g_capab.supp_mcs_nss[i] &= ~mask; 496362306a36Sopenharmony_ci s1g_capab.supp_mcs_nss[i] |= 496462306a36Sopenharmony_ci ifmgd->s1g_capa.supp_mcs_nss[i] & mask; 496562306a36Sopenharmony_ci } 496662306a36Sopenharmony_ci 496762306a36Sopenharmony_ci pos = skb_put(skb, 2 + sizeof(s1g_capab)); 496862306a36Sopenharmony_ci *pos++ = WLAN_EID_S1G_CAPABILITIES; 496962306a36Sopenharmony_ci *pos++ = sizeof(s1g_capab); 497062306a36Sopenharmony_ci 497162306a36Sopenharmony_ci memcpy(pos, &s1g_capab, sizeof(s1g_capab)); 497262306a36Sopenharmony_ci} 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_civoid ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, 497562306a36Sopenharmony_ci struct sk_buff *skb) 497662306a36Sopenharmony_ci{ 497762306a36Sopenharmony_ci u8 *pos = skb_put(skb, 3); 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ci *pos++ = WLAN_EID_AID_REQUEST; 498062306a36Sopenharmony_ci *pos++ = 1; 498162306a36Sopenharmony_ci *pos++ = 0; 498262306a36Sopenharmony_ci} 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_ciu8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) 498562306a36Sopenharmony_ci{ 498662306a36Sopenharmony_ci *buf++ = WLAN_EID_VENDOR_SPECIFIC; 498762306a36Sopenharmony_ci *buf++ = 7; /* len */ 498862306a36Sopenharmony_ci *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ 498962306a36Sopenharmony_ci *buf++ = 0x50; 499062306a36Sopenharmony_ci *buf++ = 0xf2; 499162306a36Sopenharmony_ci *buf++ = 2; /* WME */ 499262306a36Sopenharmony_ci *buf++ = 0; /* WME info */ 499362306a36Sopenharmony_ci *buf++ = 1; /* WME ver */ 499462306a36Sopenharmony_ci *buf++ = qosinfo; /* U-APSD no in use */ 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci return buf; 499762306a36Sopenharmony_ci} 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_civoid ieee80211_txq_get_depth(struct ieee80211_txq *txq, 500062306a36Sopenharmony_ci unsigned long *frame_cnt, 500162306a36Sopenharmony_ci unsigned long *byte_cnt) 500262306a36Sopenharmony_ci{ 500362306a36Sopenharmony_ci struct txq_info *txqi = to_txq_info(txq); 500462306a36Sopenharmony_ci u32 frag_cnt = 0, frag_bytes = 0; 500562306a36Sopenharmony_ci struct sk_buff *skb; 500662306a36Sopenharmony_ci 500762306a36Sopenharmony_ci skb_queue_walk(&txqi->frags, skb) { 500862306a36Sopenharmony_ci frag_cnt++; 500962306a36Sopenharmony_ci frag_bytes += skb->len; 501062306a36Sopenharmony_ci } 501162306a36Sopenharmony_ci 501262306a36Sopenharmony_ci if (frame_cnt) 501362306a36Sopenharmony_ci *frame_cnt = txqi->tin.backlog_packets + frag_cnt; 501462306a36Sopenharmony_ci 501562306a36Sopenharmony_ci if (byte_cnt) 501662306a36Sopenharmony_ci *byte_cnt = txqi->tin.backlog_bytes + frag_bytes; 501762306a36Sopenharmony_ci} 501862306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_txq_get_depth); 501962306a36Sopenharmony_ci 502062306a36Sopenharmony_ciconst u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = { 502162306a36Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, 502262306a36Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_VI, 502362306a36Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_BE, 502462306a36Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_BK 502562306a36Sopenharmony_ci}; 502662306a36Sopenharmony_ci 502762306a36Sopenharmony_ciu16 ieee80211_encode_usf(int listen_interval) 502862306a36Sopenharmony_ci{ 502962306a36Sopenharmony_ci static const int listen_int_usf[] = { 1, 10, 1000, 10000 }; 503062306a36Sopenharmony_ci u16 ui, usf = 0; 503162306a36Sopenharmony_ci 503262306a36Sopenharmony_ci /* find greatest USF */ 503362306a36Sopenharmony_ci while (usf < IEEE80211_MAX_USF) { 503462306a36Sopenharmony_ci if (listen_interval % listen_int_usf[usf + 1]) 503562306a36Sopenharmony_ci break; 503662306a36Sopenharmony_ci usf += 1; 503762306a36Sopenharmony_ci } 503862306a36Sopenharmony_ci ui = listen_interval / listen_int_usf[usf]; 503962306a36Sopenharmony_ci 504062306a36Sopenharmony_ci /* error if there is a remainder. Should've been checked by user */ 504162306a36Sopenharmony_ci WARN_ON_ONCE(ui > IEEE80211_MAX_UI); 504262306a36Sopenharmony_ci listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) | 504362306a36Sopenharmony_ci FIELD_PREP(LISTEN_INT_UI, ui); 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci return (u16) listen_interval; 504662306a36Sopenharmony_ci} 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ciu8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) 504962306a36Sopenharmony_ci{ 505062306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 505162306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap; 505262306a36Sopenharmony_ci struct ieee80211_supported_band *sband; 505362306a36Sopenharmony_ci bool is_ap; 505462306a36Sopenharmony_ci u8 n; 505562306a36Sopenharmony_ci 505662306a36Sopenharmony_ci sband = ieee80211_get_sband(sdata); 505762306a36Sopenharmony_ci if (!sband) 505862306a36Sopenharmony_ci return 0; 505962306a36Sopenharmony_ci 506062306a36Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 506162306a36Sopenharmony_ci eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype); 506262306a36Sopenharmony_ci if (!he_cap || !eht_cap) 506362306a36Sopenharmony_ci return 0; 506462306a36Sopenharmony_ci 506562306a36Sopenharmony_ci is_ap = iftype == NL80211_IFTYPE_AP || 506662306a36Sopenharmony_ci iftype == NL80211_IFTYPE_P2P_GO; 506762306a36Sopenharmony_ci 506862306a36Sopenharmony_ci n = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, 506962306a36Sopenharmony_ci &eht_cap->eht_cap_elem, 507062306a36Sopenharmony_ci is_ap); 507162306a36Sopenharmony_ci return 2 + 1 + 507262306a36Sopenharmony_ci sizeof(eht_cap->eht_cap_elem) + n + 507362306a36Sopenharmony_ci ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], 507462306a36Sopenharmony_ci eht_cap->eht_cap_elem.phy_cap_info); 507562306a36Sopenharmony_ci return 0; 507662306a36Sopenharmony_ci} 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ciu8 *ieee80211_ie_build_eht_cap(u8 *pos, 507962306a36Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap, 508062306a36Sopenharmony_ci const struct ieee80211_sta_eht_cap *eht_cap, 508162306a36Sopenharmony_ci u8 *end, 508262306a36Sopenharmony_ci bool for_ap) 508362306a36Sopenharmony_ci{ 508462306a36Sopenharmony_ci u8 mcs_nss_len, ppet_len; 508562306a36Sopenharmony_ci u8 ie_len; 508662306a36Sopenharmony_ci u8 *orig_pos = pos; 508762306a36Sopenharmony_ci 508862306a36Sopenharmony_ci /* Make sure we have place for the IE */ 508962306a36Sopenharmony_ci if (!he_cap || !eht_cap) 509062306a36Sopenharmony_ci return orig_pos; 509162306a36Sopenharmony_ci 509262306a36Sopenharmony_ci mcs_nss_len = ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem, 509362306a36Sopenharmony_ci &eht_cap->eht_cap_elem, 509462306a36Sopenharmony_ci for_ap); 509562306a36Sopenharmony_ci ppet_len = ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0], 509662306a36Sopenharmony_ci eht_cap->eht_cap_elem.phy_cap_info); 509762306a36Sopenharmony_ci 509862306a36Sopenharmony_ci ie_len = 2 + 1 + sizeof(eht_cap->eht_cap_elem) + mcs_nss_len + ppet_len; 509962306a36Sopenharmony_ci if ((end - pos) < ie_len) 510062306a36Sopenharmony_ci return orig_pos; 510162306a36Sopenharmony_ci 510262306a36Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 510362306a36Sopenharmony_ci *pos++ = ie_len - 2; 510462306a36Sopenharmony_ci *pos++ = WLAN_EID_EXT_EHT_CAPABILITY; 510562306a36Sopenharmony_ci 510662306a36Sopenharmony_ci /* Fixed data */ 510762306a36Sopenharmony_ci memcpy(pos, &eht_cap->eht_cap_elem, sizeof(eht_cap->eht_cap_elem)); 510862306a36Sopenharmony_ci pos += sizeof(eht_cap->eht_cap_elem); 510962306a36Sopenharmony_ci 511062306a36Sopenharmony_ci memcpy(pos, &eht_cap->eht_mcs_nss_supp, mcs_nss_len); 511162306a36Sopenharmony_ci pos += mcs_nss_len; 511262306a36Sopenharmony_ci 511362306a36Sopenharmony_ci if (ppet_len) { 511462306a36Sopenharmony_ci memcpy(pos, &eht_cap->eht_ppe_thres, ppet_len); 511562306a36Sopenharmony_ci pos += ppet_len; 511662306a36Sopenharmony_ci } 511762306a36Sopenharmony_ci 511862306a36Sopenharmony_ci return pos; 511962306a36Sopenharmony_ci} 512062306a36Sopenharmony_ci 512162306a36Sopenharmony_civoid ieee80211_fragment_element(struct sk_buff *skb, u8 *len_pos, u8 frag_id) 512262306a36Sopenharmony_ci{ 512362306a36Sopenharmony_ci unsigned int elem_len; 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_ci if (!len_pos) 512662306a36Sopenharmony_ci return; 512762306a36Sopenharmony_ci 512862306a36Sopenharmony_ci elem_len = skb->data + skb->len - len_pos - 1; 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci while (elem_len > 255) { 513162306a36Sopenharmony_ci /* this one is 255 */ 513262306a36Sopenharmony_ci *len_pos = 255; 513362306a36Sopenharmony_ci /* remaining data gets smaller */ 513462306a36Sopenharmony_ci elem_len -= 255; 513562306a36Sopenharmony_ci /* make space for the fragment ID/len in SKB */ 513662306a36Sopenharmony_ci skb_put(skb, 2); 513762306a36Sopenharmony_ci /* shift back the remaining data to place fragment ID/len */ 513862306a36Sopenharmony_ci memmove(len_pos + 255 + 3, len_pos + 255 + 1, elem_len); 513962306a36Sopenharmony_ci /* place the fragment ID */ 514062306a36Sopenharmony_ci len_pos += 255 + 1; 514162306a36Sopenharmony_ci *len_pos = frag_id; 514262306a36Sopenharmony_ci /* and point to fragment length to update later */ 514362306a36Sopenharmony_ci len_pos++; 514462306a36Sopenharmony_ci } 514562306a36Sopenharmony_ci 514662306a36Sopenharmony_ci *len_pos = elem_len; 514762306a36Sopenharmony_ci} 5148