18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2002-2005, Instant802 Networks, Inc. 48c2ecf20Sopenharmony_ci * Copyright 2005-2006, Devicescape Software, Inc. 58c2ecf20Sopenharmony_ci * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 68c2ecf20Sopenharmony_ci * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 78c2ecf20Sopenharmony_ci * Copyright 2013-2014 Intel Mobile Communications GmbH 88c2ecf20Sopenharmony_ci * Copyright (C) 2015-2017 Intel Deutschland GmbH 98c2ecf20Sopenharmony_ci * Copyright (C) 2018-2020 Intel Corporation 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * utilities for mac80211 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <net/mac80211.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 208c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 218c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 228c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 238c2ecf20Sopenharmony_ci#include <linux/crc32.h> 248c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 258c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 268c2ecf20Sopenharmony_ci#include <net/rtnetlink.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 298c2ecf20Sopenharmony_ci#include "driver-ops.h" 308c2ecf20Sopenharmony_ci#include "rate.h" 318c2ecf20Sopenharmony_ci#include "mesh.h" 328c2ecf20Sopenharmony_ci#include "wme.h" 338c2ecf20Sopenharmony_ci#include "led.h" 348c2ecf20Sopenharmony_ci#include "wep.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* privid for wiphys to determine whether they belong to us or not */ 378c2ecf20Sopenharmony_ciconst void *const mac80211_wiphy_privid = &mac80211_wiphy_privid; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct ieee80211_hw *wiphy_to_ieee80211_hw(struct wiphy *wiphy) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct ieee80211_local *local; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci local = wiphy_priv(wiphy); 448c2ecf20Sopenharmony_ci return &local->hw; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_to_ieee80211_hw); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciu8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, 498c2ecf20Sopenharmony_ci enum nl80211_iftype type) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci __le16 fc = hdr->frame_control; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (ieee80211_is_data(fc)) { 548c2ecf20Sopenharmony_ci if (len < 24) /* drop incorrect hdr len (data) */ 558c2ecf20Sopenharmony_ci return NULL; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (ieee80211_has_a4(fc)) 588c2ecf20Sopenharmony_ci return NULL; 598c2ecf20Sopenharmony_ci if (ieee80211_has_tods(fc)) 608c2ecf20Sopenharmony_ci return hdr->addr1; 618c2ecf20Sopenharmony_ci if (ieee80211_has_fromds(fc)) 628c2ecf20Sopenharmony_ci return hdr->addr2; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return hdr->addr3; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (ieee80211_is_s1g_beacon(fc)) { 688c2ecf20Sopenharmony_ci struct ieee80211_ext *ext = (void *) hdr; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return ext->u.s1g_beacon.sa; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (ieee80211_is_mgmt(fc)) { 748c2ecf20Sopenharmony_ci if (len < 24) /* drop incorrect hdr len (mgmt) */ 758c2ecf20Sopenharmony_ci return NULL; 768c2ecf20Sopenharmony_ci return hdr->addr3; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (ieee80211_is_ctl(fc)) { 808c2ecf20Sopenharmony_ci if (ieee80211_is_pspoll(fc)) 818c2ecf20Sopenharmony_ci return hdr->addr1; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ieee80211_is_back_req(fc)) { 848c2ecf20Sopenharmony_ci switch (type) { 858c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 868c2ecf20Sopenharmony_ci return hdr->addr2; 878c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 888c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 898c2ecf20Sopenharmony_ci return hdr->addr1; 908c2ecf20Sopenharmony_ci default: 918c2ecf20Sopenharmony_ci break; /* fall through to the return */ 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return NULL; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_get_bssid); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_civoid ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct sk_buff *skb; 1038c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci skb_queue_walk(&tx->skbs, skb) { 1068c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *) skb->data; 1078c2ecf20Sopenharmony_ci hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciint ieee80211_frame_duration(enum nl80211_band band, size_t len, 1128c2ecf20Sopenharmony_ci int rate, int erp, int short_preamble, 1138c2ecf20Sopenharmony_ci int shift) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int dur; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* calculate duration (in microseconds, rounded up to next higher 1188c2ecf20Sopenharmony_ci * integer if it includes a fractional microsecond) to send frame of 1198c2ecf20Sopenharmony_ci * len bytes (does not include FCS) at the given rate. Duration will 1208c2ecf20Sopenharmony_ci * also include SIFS. 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * rate is in 100 kbps, so divident is multiplied by 10 in the 1238c2ecf20Sopenharmony_ci * DIV_ROUND_UP() operations. 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and 1268c2ecf20Sopenharmony_ci * is assumed to be 0 otherwise. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ || erp) { 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * OFDM: 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * N_DBPS = DATARATE x 4 1348c2ecf20Sopenharmony_ci * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) 1358c2ecf20Sopenharmony_ci * (16 = SIGNAL time, 6 = tail bits) 1368c2ecf20Sopenharmony_ci * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * T_SYM = 4 usec 1398c2ecf20Sopenharmony_ci * 802.11a - 18.5.2: aSIFSTime = 16 usec 1408c2ecf20Sopenharmony_ci * 802.11g - 19.8.4: aSIFSTime = 10 usec + 1418c2ecf20Sopenharmony_ci * signal ext = 6 usec 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci dur = 16; /* SIFS + signal ext */ 1448c2ecf20Sopenharmony_ci dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */ 1458c2ecf20Sopenharmony_ci dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */ 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* IEEE 802.11-2012 18.3.2.4: all values above are: 1488c2ecf20Sopenharmony_ci * * times 4 for 5 MHz 1498c2ecf20Sopenharmony_ci * * times 2 for 10 MHz 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci dur *= 1 << shift; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* rates should already consider the channel bandwidth, 1548c2ecf20Sopenharmony_ci * don't apply divisor again. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, 1578c2ecf20Sopenharmony_ci 4 * rate); /* T_SYM x N_SYM */ 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * 802.11b or 802.11g with 802.11b compatibility: 1618c2ecf20Sopenharmony_ci * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + 1628c2ecf20Sopenharmony_ci * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 1658c2ecf20Sopenharmony_ci * aSIFSTime = 10 usec 1668c2ecf20Sopenharmony_ci * aPreambleLength = 144 usec or 72 usec with short preamble 1678c2ecf20Sopenharmony_ci * aPLCPHeaderLength = 48 usec or 24 usec with short preamble 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci dur = 10; /* aSIFSTime = 10 usec */ 1708c2ecf20Sopenharmony_ci dur += short_preamble ? (72 + 24) : (144 + 48); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return dur; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* Exported duration function for driver use */ 1798c2ecf20Sopenharmony_ci__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, 1808c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 1818c2ecf20Sopenharmony_ci enum nl80211_band band, 1828c2ecf20Sopenharmony_ci size_t frame_len, 1838c2ecf20Sopenharmony_ci struct ieee80211_rate *rate) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 1868c2ecf20Sopenharmony_ci u16 dur; 1878c2ecf20Sopenharmony_ci int erp, shift = 0; 1888c2ecf20Sopenharmony_ci bool short_preamble = false; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci erp = 0; 1918c2ecf20Sopenharmony_ci if (vif) { 1928c2ecf20Sopenharmony_ci sdata = vif_to_sdata(vif); 1938c2ecf20Sopenharmony_ci short_preamble = sdata->vif.bss_conf.use_short_preamble; 1948c2ecf20Sopenharmony_ci if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 1958c2ecf20Sopenharmony_ci erp = rate->flags & IEEE80211_RATE_ERP_G; 1968c2ecf20Sopenharmony_ci shift = ieee80211_vif_get_shift(vif); 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp, 2008c2ecf20Sopenharmony_ci short_preamble, shift); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return cpu_to_le16(dur); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_generic_frame_duration); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, 2078c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, size_t frame_len, 2088c2ecf20Sopenharmony_ci const struct ieee80211_tx_info *frame_txctl) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 2118c2ecf20Sopenharmony_ci struct ieee80211_rate *rate; 2128c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 2138c2ecf20Sopenharmony_ci bool short_preamble; 2148c2ecf20Sopenharmony_ci int erp, shift = 0, bitrate; 2158c2ecf20Sopenharmony_ci u16 dur; 2168c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[frame_txctl->band]; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci short_preamble = false; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci erp = 0; 2258c2ecf20Sopenharmony_ci if (vif) { 2268c2ecf20Sopenharmony_ci sdata = vif_to_sdata(vif); 2278c2ecf20Sopenharmony_ci short_preamble = sdata->vif.bss_conf.use_short_preamble; 2288c2ecf20Sopenharmony_ci if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2298c2ecf20Sopenharmony_ci erp = rate->flags & IEEE80211_RATE_ERP_G; 2308c2ecf20Sopenharmony_ci shift = ieee80211_vif_get_shift(vif); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* CTS duration */ 2368c2ecf20Sopenharmony_ci dur = ieee80211_frame_duration(sband->band, 10, bitrate, 2378c2ecf20Sopenharmony_ci erp, short_preamble, shift); 2388c2ecf20Sopenharmony_ci /* Data frame duration */ 2398c2ecf20Sopenharmony_ci dur += ieee80211_frame_duration(sband->band, frame_len, bitrate, 2408c2ecf20Sopenharmony_ci erp, short_preamble, shift); 2418c2ecf20Sopenharmony_ci /* ACK duration */ 2428c2ecf20Sopenharmony_ci dur += ieee80211_frame_duration(sband->band, 10, bitrate, 2438c2ecf20Sopenharmony_ci erp, short_preamble, shift); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return cpu_to_le16(dur); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_rts_duration); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, 2508c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 2518c2ecf20Sopenharmony_ci size_t frame_len, 2528c2ecf20Sopenharmony_ci const struct ieee80211_tx_info *frame_txctl) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 2558c2ecf20Sopenharmony_ci struct ieee80211_rate *rate; 2568c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 2578c2ecf20Sopenharmony_ci bool short_preamble; 2588c2ecf20Sopenharmony_ci int erp, shift = 0, bitrate; 2598c2ecf20Sopenharmony_ci u16 dur; 2608c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[frame_txctl->band]; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci short_preamble = false; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci rate = &sband->bitrates[frame_txctl->control.rts_cts_rate_idx]; 2678c2ecf20Sopenharmony_ci erp = 0; 2688c2ecf20Sopenharmony_ci if (vif) { 2698c2ecf20Sopenharmony_ci sdata = vif_to_sdata(vif); 2708c2ecf20Sopenharmony_ci short_preamble = sdata->vif.bss_conf.use_short_preamble; 2718c2ecf20Sopenharmony_ci if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE) 2728c2ecf20Sopenharmony_ci erp = rate->flags & IEEE80211_RATE_ERP_G; 2738c2ecf20Sopenharmony_ci shift = ieee80211_vif_get_shift(vif); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Data frame duration */ 2798c2ecf20Sopenharmony_ci dur = ieee80211_frame_duration(sband->band, frame_len, bitrate, 2808c2ecf20Sopenharmony_ci erp, short_preamble, shift); 2818c2ecf20Sopenharmony_ci if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) { 2828c2ecf20Sopenharmony_ci /* ACK duration */ 2838c2ecf20Sopenharmony_ci dur += ieee80211_frame_duration(sband->band, 10, bitrate, 2848c2ecf20Sopenharmony_ci erp, short_preamble, shift); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return cpu_to_le16(dur); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_ctstoself_duration); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 2948c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = &sdata->vif; 2958c2ecf20Sopenharmony_ci struct fq *fq = &local->fq; 2968c2ecf20Sopenharmony_ci struct ps_data *ps = NULL; 2978c2ecf20Sopenharmony_ci struct txq_info *txqi; 2988c2ecf20Sopenharmony_ci struct sta_info *sta; 2998c2ecf20Sopenharmony_ci int i; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci local_bh_disable(); 3028c2ecf20Sopenharmony_ci spin_lock(&fq->lock); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) 3058c2ecf20Sopenharmony_ci ps = &sdata->bss->ps; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci sdata->vif.txqs_stopped[ac] = false; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 3108c2ecf20Sopenharmony_ci if (sdata != sta->sdata) 3118c2ecf20Sopenharmony_ci continue; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { 3148c2ecf20Sopenharmony_ci struct ieee80211_txq *txq = sta->sta.txq[i]; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!txq) 3178c2ecf20Sopenharmony_ci continue; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci txqi = to_txq_info(txq); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (ac != txq->ac) 3228c2ecf20Sopenharmony_ci continue; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, 3258c2ecf20Sopenharmony_ci &txqi->flags)) 3268c2ecf20Sopenharmony_ci continue; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci spin_unlock(&fq->lock); 3298c2ecf20Sopenharmony_ci drv_wake_tx_queue(local, txqi); 3308c2ecf20Sopenharmony_ci spin_lock(&fq->lock); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!vif->txq) 3358c2ecf20Sopenharmony_ci goto out; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci txqi = to_txq_info(vif->txq); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) || 3408c2ecf20Sopenharmony_ci (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) 3418c2ecf20Sopenharmony_ci goto out; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_unlock(&fq->lock); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci drv_wake_tx_queue(local, txqi); 3468c2ecf20Sopenharmony_ci local_bh_enable(); 3478c2ecf20Sopenharmony_ci return; 3488c2ecf20Sopenharmony_ciout: 3498c2ecf20Sopenharmony_ci spin_unlock(&fq->lock); 3508c2ecf20Sopenharmony_ci local_bh_enable(); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void 3548c2ecf20Sopenharmony_ci__releases(&local->queue_stop_reason_lock) 3558c2ecf20Sopenharmony_ci__acquires(&local->queue_stop_reason_lock) 3568c2ecf20Sopenharmony_ci_ieee80211_wake_txqs(struct ieee80211_local *local, unsigned long *flags) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 3598c2ecf20Sopenharmony_ci int n_acs = IEEE80211_NUM_ACS; 3608c2ecf20Sopenharmony_ci int i; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci rcu_read_lock(); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 3658c2ecf20Sopenharmony_ci n_acs = 1; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci for (i = 0; i < local->hw.queues; i++) { 3688c2ecf20Sopenharmony_ci if (local->queue_stop_reasons[i]) 3698c2ecf20Sopenharmony_ci continue; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, *flags); 3728c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 3738c2ecf20Sopenharmony_ci int ac; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci for (ac = 0; ac < n_acs; ac++) { 3768c2ecf20Sopenharmony_ci int ac_queue = sdata->vif.hw_queue[ac]; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (ac_queue == i || 3798c2ecf20Sopenharmony_ci sdata->vif.cab_queue == i) 3808c2ecf20Sopenharmony_ci __ieee80211_wake_txqs(sdata, ac); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, *flags); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci rcu_read_unlock(); 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_civoid ieee80211_wake_txqs(unsigned long data) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct ieee80211_local *local = (struct ieee80211_local *)data; 3928c2ecf20Sopenharmony_ci unsigned long flags; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 3958c2ecf20Sopenharmony_ci _ieee80211_wake_txqs(local, &flags); 3968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_civoid ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 4028c2ecf20Sopenharmony_ci int n_acs = IEEE80211_NUM_ACS; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (local->ops->wake_tx_queue) 4058c2ecf20Sopenharmony_ci return; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 4088c2ecf20Sopenharmony_ci n_acs = 1; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 4118c2ecf20Sopenharmony_ci int ac; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!sdata->dev) 4148c2ecf20Sopenharmony_ci continue; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && 4178c2ecf20Sopenharmony_ci local->queue_stop_reasons[sdata->vif.cab_queue] != 0) 4188c2ecf20Sopenharmony_ci continue; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci for (ac = 0; ac < n_acs; ac++) { 4218c2ecf20Sopenharmony_ci int ac_queue = sdata->vif.hw_queue[ac]; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (ac_queue == queue || 4248c2ecf20Sopenharmony_ci (sdata->vif.cab_queue == queue && 4258c2ecf20Sopenharmony_ci local->queue_stop_reasons[ac_queue] == 0 && 4268c2ecf20Sopenharmony_ci skb_queue_empty(&local->pending[ac_queue]))) 4278c2ecf20Sopenharmony_ci netif_wake_subqueue(sdata->dev, ac); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, 4338c2ecf20Sopenharmony_ci enum queue_stop_reason reason, 4348c2ecf20Sopenharmony_ci bool refcounted, 4358c2ecf20Sopenharmony_ci unsigned long *flags) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci trace_wake_queue(local, queue, reason); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (WARN_ON(queue >= hw->queues)) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (!test_bit(reason, &local->queue_stop_reasons[queue])) 4458c2ecf20Sopenharmony_ci return; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (!refcounted) { 4488c2ecf20Sopenharmony_ci local->q_stop_reasons[queue][reason] = 0; 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci local->q_stop_reasons[queue][reason]--; 4518c2ecf20Sopenharmony_ci if (WARN_ON(local->q_stop_reasons[queue][reason] < 0)) 4528c2ecf20Sopenharmony_ci local->q_stop_reasons[queue][reason] = 0; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (local->q_stop_reasons[queue][reason] == 0) 4568c2ecf20Sopenharmony_ci __clear_bit(reason, &local->queue_stop_reasons[queue]); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (local->queue_stop_reasons[queue] != 0) 4598c2ecf20Sopenharmony_ci /* someone still has this queue stopped */ 4608c2ecf20Sopenharmony_ci return; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (skb_queue_empty(&local->pending[queue])) { 4638c2ecf20Sopenharmony_ci rcu_read_lock(); 4648c2ecf20Sopenharmony_ci ieee80211_propagate_queue_wake(local, queue); 4658c2ecf20Sopenharmony_ci rcu_read_unlock(); 4668c2ecf20Sopenharmony_ci } else 4678c2ecf20Sopenharmony_ci tasklet_schedule(&local->tx_pending_tasklet); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* 4708c2ecf20Sopenharmony_ci * Calling _ieee80211_wake_txqs here can be a problem because it may 4718c2ecf20Sopenharmony_ci * release queue_stop_reason_lock which has been taken by 4728c2ecf20Sopenharmony_ci * __ieee80211_wake_queue's caller. It is certainly not very nice to 4738c2ecf20Sopenharmony_ci * release someone's lock, but it is fine because all the callers of 4748c2ecf20Sopenharmony_ci * __ieee80211_wake_queue call it right before releasing the lock. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci if (local->ops->wake_tx_queue) { 4778c2ecf20Sopenharmony_ci if (reason == IEEE80211_QUEUE_STOP_REASON_DRIVER) 4788c2ecf20Sopenharmony_ci tasklet_schedule(&local->wake_txqs_tasklet); 4798c2ecf20Sopenharmony_ci else 4808c2ecf20Sopenharmony_ci _ieee80211_wake_txqs(local, flags); 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_civoid ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, 4858c2ecf20Sopenharmony_ci enum queue_stop_reason reason, 4868c2ecf20Sopenharmony_ci bool refcounted) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 4898c2ecf20Sopenharmony_ci unsigned long flags; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 4928c2ecf20Sopenharmony_ci __ieee80211_wake_queue(hw, queue, reason, refcounted, &flags); 4938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_civoid ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci ieee80211_wake_queue_by_reason(hw, queue, 4998c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 5008c2ecf20Sopenharmony_ci false); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_wake_queue); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, 5058c2ecf20Sopenharmony_ci enum queue_stop_reason reason, 5068c2ecf20Sopenharmony_ci bool refcounted) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 5098c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 5108c2ecf20Sopenharmony_ci int n_acs = IEEE80211_NUM_ACS; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci trace_stop_queue(local, queue, reason); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (WARN_ON(queue >= hw->queues)) 5158c2ecf20Sopenharmony_ci return; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!refcounted) 5188c2ecf20Sopenharmony_ci local->q_stop_reasons[queue][reason] = 1; 5198c2ecf20Sopenharmony_ci else 5208c2ecf20Sopenharmony_ci local->q_stop_reasons[queue][reason]++; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue])) 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 5268c2ecf20Sopenharmony_ci n_acs = 1; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci rcu_read_lock(); 5298c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 5308c2ecf20Sopenharmony_ci int ac; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (!sdata->dev) 5338c2ecf20Sopenharmony_ci continue; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci for (ac = 0; ac < n_acs; ac++) { 5368c2ecf20Sopenharmony_ci if (sdata->vif.hw_queue[ac] == queue || 5378c2ecf20Sopenharmony_ci sdata->vif.cab_queue == queue) { 5388c2ecf20Sopenharmony_ci if (!local->ops->wake_tx_queue) { 5398c2ecf20Sopenharmony_ci netif_stop_subqueue(sdata->dev, ac); 5408c2ecf20Sopenharmony_ci continue; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci spin_lock(&local->fq.lock); 5438c2ecf20Sopenharmony_ci sdata->vif.txqs_stopped[ac] = true; 5448c2ecf20Sopenharmony_ci spin_unlock(&local->fq.lock); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci rcu_read_unlock(); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_civoid ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, 5528c2ecf20Sopenharmony_ci enum queue_stop_reason reason, 5538c2ecf20Sopenharmony_ci bool refcounted) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 5568c2ecf20Sopenharmony_ci unsigned long flags; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 5598c2ecf20Sopenharmony_ci __ieee80211_stop_queue(hw, queue, reason, refcounted); 5608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_civoid ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci ieee80211_stop_queue_by_reason(hw, queue, 5668c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 5678c2ecf20Sopenharmony_ci false); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_stop_queue); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_civoid ieee80211_add_pending_skb(struct ieee80211_local *local, 5728c2ecf20Sopenharmony_ci struct sk_buff *skb) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = &local->hw; 5758c2ecf20Sopenharmony_ci unsigned long flags; 5768c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 5778c2ecf20Sopenharmony_ci int queue = info->hw_queue; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (WARN_ON(!info->control.vif)) { 5808c2ecf20Sopenharmony_ci ieee80211_free_txskb(&local->hw, skb); 5818c2ecf20Sopenharmony_ci return; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 5858c2ecf20Sopenharmony_ci __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 5868c2ecf20Sopenharmony_ci false); 5878c2ecf20Sopenharmony_ci __skb_queue_tail(&local->pending[queue], skb); 5888c2ecf20Sopenharmony_ci __ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 5898c2ecf20Sopenharmony_ci false, &flags); 5908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_civoid ieee80211_add_pending_skbs(struct ieee80211_local *local, 5948c2ecf20Sopenharmony_ci struct sk_buff_head *skbs) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = &local->hw; 5978c2ecf20Sopenharmony_ci struct sk_buff *skb; 5988c2ecf20Sopenharmony_ci unsigned long flags; 5998c2ecf20Sopenharmony_ci int queue, i; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 6028c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(skbs))) { 6038c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (WARN_ON(!info->control.vif)) { 6068c2ecf20Sopenharmony_ci ieee80211_free_txskb(&local->hw, skb); 6078c2ecf20Sopenharmony_ci continue; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci queue = info->hw_queue; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci __ieee80211_stop_queue(hw, queue, 6138c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 6148c2ecf20Sopenharmony_ci false); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci __skb_queue_tail(&local->pending[queue], skb); 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci for (i = 0; i < hw->queues; i++) 6208c2ecf20Sopenharmony_ci __ieee80211_wake_queue(hw, i, 6218c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_SKB_ADD, 6228c2ecf20Sopenharmony_ci false, &flags); 6238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_civoid ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw, 6278c2ecf20Sopenharmony_ci unsigned long queues, 6288c2ecf20Sopenharmony_ci enum queue_stop_reason reason, 6298c2ecf20Sopenharmony_ci bool refcounted) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 6328c2ecf20Sopenharmony_ci unsigned long flags; 6338c2ecf20Sopenharmony_ci int i; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci for_each_set_bit(i, &queues, hw->queues) 6388c2ecf20Sopenharmony_ci __ieee80211_stop_queue(hw, i, reason, refcounted); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_civoid ieee80211_stop_queues(struct ieee80211_hw *hw) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci ieee80211_stop_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 6468c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 6478c2ecf20Sopenharmony_ci false); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_stop_queues); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ciint ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 6548c2ecf20Sopenharmony_ci unsigned long flags; 6558c2ecf20Sopenharmony_ci int ret; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (WARN_ON(queue >= hw->queues)) 6588c2ecf20Sopenharmony_ci return true; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 6618c2ecf20Sopenharmony_ci ret = test_bit(IEEE80211_QUEUE_STOP_REASON_DRIVER, 6628c2ecf20Sopenharmony_ci &local->queue_stop_reasons[queue]); 6638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_queue_stopped); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_civoid ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, 6698c2ecf20Sopenharmony_ci unsigned long queues, 6708c2ecf20Sopenharmony_ci enum queue_stop_reason reason, 6718c2ecf20Sopenharmony_ci bool refcounted) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 6748c2ecf20Sopenharmony_ci unsigned long flags; 6758c2ecf20Sopenharmony_ci int i; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->queue_stop_reason_lock, flags); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci for_each_set_bit(i, &queues, hw->queues) 6808c2ecf20Sopenharmony_ci __ieee80211_wake_queue(hw, i, reason, refcounted, &flags); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_civoid ieee80211_wake_queues(struct ieee80211_hw *hw) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 6888c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_DRIVER, 6898c2ecf20Sopenharmony_ci false); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_wake_queues); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic unsigned int 6948c2ecf20Sopenharmony_ciieee80211_get_vif_queues(struct ieee80211_local *local, 6958c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci unsigned int queues; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (sdata && ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) { 7008c2ecf20Sopenharmony_ci int ac; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci queues = 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 7058c2ecf20Sopenharmony_ci queues |= BIT(sdata->vif.hw_queue[ac]); 7068c2ecf20Sopenharmony_ci if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE) 7078c2ecf20Sopenharmony_ci queues |= BIT(sdata->vif.cab_queue); 7088c2ecf20Sopenharmony_ci } else { 7098c2ecf20Sopenharmony_ci /* all queues */ 7108c2ecf20Sopenharmony_ci queues = BIT(local->hw.queues) - 1; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return queues; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_civoid __ieee80211_flush_queues(struct ieee80211_local *local, 7178c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 7188c2ecf20Sopenharmony_ci unsigned int queues, bool drop) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci if (!local->ops->flush) 7218c2ecf20Sopenharmony_ci return; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* 7248c2ecf20Sopenharmony_ci * If no queue was set, or if the HW doesn't support 7258c2ecf20Sopenharmony_ci * IEEE80211_HW_QUEUE_CONTROL - flush all queues 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci if (!queues || !ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) 7288c2ecf20Sopenharmony_ci queues = ieee80211_get_vif_queues(local, sdata); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci ieee80211_stop_queues_by_reason(&local->hw, queues, 7318c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_FLUSH, 7328c2ecf20Sopenharmony_ci false); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci drv_flush(local, sdata, queues, drop); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci ieee80211_wake_queues_by_reason(&local->hw, queues, 7378c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_FLUSH, 7388c2ecf20Sopenharmony_ci false); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_civoid ieee80211_flush_queues(struct ieee80211_local *local, 7428c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata, bool drop) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci __ieee80211_flush_queues(local, sdata, 0, drop); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_civoid ieee80211_stop_vif_queues(struct ieee80211_local *local, 7488c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 7498c2ecf20Sopenharmony_ci enum queue_stop_reason reason) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci ieee80211_stop_queues_by_reason(&local->hw, 7528c2ecf20Sopenharmony_ci ieee80211_get_vif_queues(local, sdata), 7538c2ecf20Sopenharmony_ci reason, true); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_civoid ieee80211_wake_vif_queues(struct ieee80211_local *local, 7578c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata, 7588c2ecf20Sopenharmony_ci enum queue_stop_reason reason) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci ieee80211_wake_queues_by_reason(&local->hw, 7618c2ecf20Sopenharmony_ci ieee80211_get_vif_queues(local, sdata), 7628c2ecf20Sopenharmony_ci reason, true); 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic void __iterate_interfaces(struct ieee80211_local *local, 7668c2ecf20Sopenharmony_ci u32 iter_flags, 7678c2ecf20Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 7688c2ecf20Sopenharmony_ci struct ieee80211_vif *vif), 7698c2ecf20Sopenharmony_ci void *data) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 7728c2ecf20Sopenharmony_ci bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) { 7758c2ecf20Sopenharmony_ci switch (sdata->vif.type) { 7768c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 7778c2ecf20Sopenharmony_ci if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE)) 7788c2ecf20Sopenharmony_ci continue; 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 7818c2ecf20Sopenharmony_ci continue; 7828c2ecf20Sopenharmony_ci default: 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && 7868c2ecf20Sopenharmony_ci active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 7878c2ecf20Sopenharmony_ci continue; 7888c2ecf20Sopenharmony_ci if ((iter_flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) && 7898c2ecf20Sopenharmony_ci !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 7908c2ecf20Sopenharmony_ci continue; 7918c2ecf20Sopenharmony_ci if (ieee80211_sdata_running(sdata) || !active_only) 7928c2ecf20Sopenharmony_ci iterator(data, sdata->vif.addr, 7938c2ecf20Sopenharmony_ci &sdata->vif); 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci sdata = rcu_dereference_check(local->monitor_sdata, 7978c2ecf20Sopenharmony_ci lockdep_is_held(&local->iflist_mtx) || 7988c2ecf20Sopenharmony_ci lockdep_rtnl_is_held()); 7998c2ecf20Sopenharmony_ci if (sdata && 8008c2ecf20Sopenharmony_ci (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only || 8018c2ecf20Sopenharmony_ci sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 8028c2ecf20Sopenharmony_ci iterator(data, sdata->vif.addr, &sdata->vif); 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_civoid ieee80211_iterate_interfaces( 8068c2ecf20Sopenharmony_ci struct ieee80211_hw *hw, u32 iter_flags, 8078c2ecf20Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 8088c2ecf20Sopenharmony_ci struct ieee80211_vif *vif), 8098c2ecf20Sopenharmony_ci void *data) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci mutex_lock(&local->iflist_mtx); 8148c2ecf20Sopenharmony_ci __iterate_interfaces(local, iter_flags, iterator, data); 8158c2ecf20Sopenharmony_ci mutex_unlock(&local->iflist_mtx); 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_civoid ieee80211_iterate_active_interfaces_atomic( 8208c2ecf20Sopenharmony_ci struct ieee80211_hw *hw, u32 iter_flags, 8218c2ecf20Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 8228c2ecf20Sopenharmony_ci struct ieee80211_vif *vif), 8238c2ecf20Sopenharmony_ci void *data) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci rcu_read_lock(); 8288c2ecf20Sopenharmony_ci __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, 8298c2ecf20Sopenharmony_ci iterator, data); 8308c2ecf20Sopenharmony_ci rcu_read_unlock(); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_civoid ieee80211_iterate_active_interfaces_rtnl( 8358c2ecf20Sopenharmony_ci struct ieee80211_hw *hw, u32 iter_flags, 8368c2ecf20Sopenharmony_ci void (*iterator)(void *data, u8 *mac, 8378c2ecf20Sopenharmony_ci struct ieee80211_vif *vif), 8388c2ecf20Sopenharmony_ci void *data) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci ASSERT_RTNL(); 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, 8458c2ecf20Sopenharmony_ci iterator, data); 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic void __iterate_stations(struct ieee80211_local *local, 8508c2ecf20Sopenharmony_ci void (*iterator)(void *data, 8518c2ecf20Sopenharmony_ci struct ieee80211_sta *sta), 8528c2ecf20Sopenharmony_ci void *data) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct sta_info *sta; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sta, &local->sta_list, list) { 8578c2ecf20Sopenharmony_ci if (!sta->uploaded) 8588c2ecf20Sopenharmony_ci continue; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci iterator(data, &sta->sta); 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_civoid ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, 8658c2ecf20Sopenharmony_ci void (*iterator)(void *data, 8668c2ecf20Sopenharmony_ci struct ieee80211_sta *sta), 8678c2ecf20Sopenharmony_ci void *data) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci rcu_read_lock(); 8728c2ecf20Sopenharmony_ci __iterate_stations(local, iterator, data); 8738c2ecf20Sopenharmony_ci rcu_read_unlock(); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistruct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (!ieee80211_sdata_running(sdata) || 8828c2ecf20Sopenharmony_ci !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 8838c2ecf20Sopenharmony_ci return NULL; 8848c2ecf20Sopenharmony_ci return &sdata->vif; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistruct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if (!vif) 8938c2ecf20Sopenharmony_ci return NULL; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci sdata = vif_to_sdata(vif); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (!ieee80211_sdata_running(sdata) || 8988c2ecf20Sopenharmony_ci !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) 8998c2ecf20Sopenharmony_ci return NULL; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci return &sdata->wdev; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci/* 9068c2ecf20Sopenharmony_ci * Nothing should have been stuffed into the workqueue during 9078c2ecf20Sopenharmony_ci * the suspend->resume cycle. Since we can't check each caller 9088c2ecf20Sopenharmony_ci * of this function if we are already quiescing / suspended, 9098c2ecf20Sopenharmony_ci * check here and don't WARN since this can actually happen when 9108c2ecf20Sopenharmony_ci * the rx path (for example) is racing against __ieee80211_suspend 9118c2ecf20Sopenharmony_ci * and suspending / quiescing was set after the rx path checked 9128c2ecf20Sopenharmony_ci * them. 9138c2ecf20Sopenharmony_ci */ 9148c2ecf20Sopenharmony_cistatic bool ieee80211_can_queue_work(struct ieee80211_local *local) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci if (local->quiescing || (local->suspended && !local->resuming)) { 9178c2ecf20Sopenharmony_ci pr_warn("queueing ieee80211 work while going to suspend\n"); 9188c2ecf20Sopenharmony_ci return false; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci return true; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_civoid ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work) 9258c2ecf20Sopenharmony_ci{ 9268c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (!ieee80211_can_queue_work(local)) 9298c2ecf20Sopenharmony_ci return; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci queue_work(local->workqueue, work); 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_queue_work); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_civoid ieee80211_queue_delayed_work(struct ieee80211_hw *hw, 9368c2ecf20Sopenharmony_ci struct delayed_work *dwork, 9378c2ecf20Sopenharmony_ci unsigned long delay) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (!ieee80211_can_queue_work(local)) 9428c2ecf20Sopenharmony_ci return; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci queue_delayed_work(local->workqueue, dwork, delay); 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_queue_delayed_work); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic void ieee80211_parse_extension_element(u32 *crc, 9498c2ecf20Sopenharmony_ci const struct element *elem, 9508c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci const void *data = elem->data + 1; 9538c2ecf20Sopenharmony_ci u8 len; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (!elem->datalen) 9568c2ecf20Sopenharmony_ci return; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci len = elem->datalen - 1; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci switch (elem->data[0]) { 9618c2ecf20Sopenharmony_ci case WLAN_EID_EXT_HE_MU_EDCA: 9628c2ecf20Sopenharmony_ci if (len >= sizeof(*elems->mu_edca_param_set)) { 9638c2ecf20Sopenharmony_ci elems->mu_edca_param_set = data; 9648c2ecf20Sopenharmony_ci if (crc) 9658c2ecf20Sopenharmony_ci *crc = crc32_be(*crc, (void *)elem, 9668c2ecf20Sopenharmony_ci elem->datalen + 2); 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci case WLAN_EID_EXT_HE_CAPABILITY: 9708c2ecf20Sopenharmony_ci elems->he_cap = data; 9718c2ecf20Sopenharmony_ci elems->he_cap_len = len; 9728c2ecf20Sopenharmony_ci break; 9738c2ecf20Sopenharmony_ci case WLAN_EID_EXT_HE_OPERATION: 9748c2ecf20Sopenharmony_ci if (len >= sizeof(*elems->he_operation) && 9758c2ecf20Sopenharmony_ci len >= ieee80211_he_oper_size(data) - 1) { 9768c2ecf20Sopenharmony_ci if (crc) 9778c2ecf20Sopenharmony_ci *crc = crc32_be(*crc, (void *)elem, 9788c2ecf20Sopenharmony_ci elem->datalen + 2); 9798c2ecf20Sopenharmony_ci elems->he_operation = data; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci break; 9828c2ecf20Sopenharmony_ci case WLAN_EID_EXT_UORA: 9838c2ecf20Sopenharmony_ci if (len >= 1) 9848c2ecf20Sopenharmony_ci elems->uora_element = data; 9858c2ecf20Sopenharmony_ci break; 9868c2ecf20Sopenharmony_ci case WLAN_EID_EXT_MAX_CHANNEL_SWITCH_TIME: 9878c2ecf20Sopenharmony_ci if (len == 3) 9888c2ecf20Sopenharmony_ci elems->max_channel_switch_time = data; 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci case WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION: 9918c2ecf20Sopenharmony_ci if (len >= sizeof(*elems->mbssid_config_ie)) 9928c2ecf20Sopenharmony_ci elems->mbssid_config_ie = data; 9938c2ecf20Sopenharmony_ci break; 9948c2ecf20Sopenharmony_ci case WLAN_EID_EXT_HE_SPR: 9958c2ecf20Sopenharmony_ci if (len >= sizeof(*elems->he_spr) && 9968c2ecf20Sopenharmony_ci len >= ieee80211_he_spr_size(data)) 9978c2ecf20Sopenharmony_ci elems->he_spr = data; 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci case WLAN_EID_EXT_HE_6GHZ_CAPA: 10008c2ecf20Sopenharmony_ci if (len >= sizeof(*elems->he_6ghz_capa)) 10018c2ecf20Sopenharmony_ci elems->he_6ghz_capa = data; 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic u32 10078c2ecf20Sopenharmony_ci_ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, 10088c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 10098c2ecf20Sopenharmony_ci u64 filter, u32 crc, 10108c2ecf20Sopenharmony_ci const struct element *check_inherit) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci const struct element *elem; 10138c2ecf20Sopenharmony_ci bool calc_crc = filter != 0; 10148c2ecf20Sopenharmony_ci DECLARE_BITMAP(seen_elems, 256); 10158c2ecf20Sopenharmony_ci const u8 *ie; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci bitmap_zero(seen_elems, 256); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci for_each_element(elem, start, len) { 10208c2ecf20Sopenharmony_ci bool elem_parse_failed; 10218c2ecf20Sopenharmony_ci u8 id = elem->id; 10228c2ecf20Sopenharmony_ci u8 elen = elem->datalen; 10238c2ecf20Sopenharmony_ci const u8 *pos = elem->data; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (check_inherit && 10268c2ecf20Sopenharmony_ci !cfg80211_is_element_inherited(elem, 10278c2ecf20Sopenharmony_ci check_inherit)) 10288c2ecf20Sopenharmony_ci continue; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci switch (id) { 10318c2ecf20Sopenharmony_ci case WLAN_EID_SSID: 10328c2ecf20Sopenharmony_ci case WLAN_EID_SUPP_RATES: 10338c2ecf20Sopenharmony_ci case WLAN_EID_FH_PARAMS: 10348c2ecf20Sopenharmony_ci case WLAN_EID_DS_PARAMS: 10358c2ecf20Sopenharmony_ci case WLAN_EID_CF_PARAMS: 10368c2ecf20Sopenharmony_ci case WLAN_EID_TIM: 10378c2ecf20Sopenharmony_ci case WLAN_EID_IBSS_PARAMS: 10388c2ecf20Sopenharmony_ci case WLAN_EID_CHALLENGE: 10398c2ecf20Sopenharmony_ci case WLAN_EID_RSN: 10408c2ecf20Sopenharmony_ci case WLAN_EID_ERP_INFO: 10418c2ecf20Sopenharmony_ci case WLAN_EID_EXT_SUPP_RATES: 10428c2ecf20Sopenharmony_ci case WLAN_EID_HT_CAPABILITY: 10438c2ecf20Sopenharmony_ci case WLAN_EID_HT_OPERATION: 10448c2ecf20Sopenharmony_ci case WLAN_EID_VHT_CAPABILITY: 10458c2ecf20Sopenharmony_ci case WLAN_EID_VHT_OPERATION: 10468c2ecf20Sopenharmony_ci case WLAN_EID_MESH_ID: 10478c2ecf20Sopenharmony_ci case WLAN_EID_MESH_CONFIG: 10488c2ecf20Sopenharmony_ci case WLAN_EID_PEER_MGMT: 10498c2ecf20Sopenharmony_ci case WLAN_EID_PREQ: 10508c2ecf20Sopenharmony_ci case WLAN_EID_PREP: 10518c2ecf20Sopenharmony_ci case WLAN_EID_PERR: 10528c2ecf20Sopenharmony_ci case WLAN_EID_RANN: 10538c2ecf20Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH: 10548c2ecf20Sopenharmony_ci case WLAN_EID_EXT_CHANSWITCH_ANN: 10558c2ecf20Sopenharmony_ci case WLAN_EID_COUNTRY: 10568c2ecf20Sopenharmony_ci case WLAN_EID_PWR_CONSTRAINT: 10578c2ecf20Sopenharmony_ci case WLAN_EID_TIMEOUT_INTERVAL: 10588c2ecf20Sopenharmony_ci case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 10598c2ecf20Sopenharmony_ci case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 10608c2ecf20Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_PARAM: 10618c2ecf20Sopenharmony_ci case WLAN_EID_EXT_CAPABILITY: 10628c2ecf20Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_TIMING: 10638c2ecf20Sopenharmony_ci case WLAN_EID_LINK_ID: 10648c2ecf20Sopenharmony_ci case WLAN_EID_BSS_MAX_IDLE_PERIOD: 10658c2ecf20Sopenharmony_ci case WLAN_EID_RSNX: 10668c2ecf20Sopenharmony_ci case WLAN_EID_S1G_BCN_COMPAT: 10678c2ecf20Sopenharmony_ci case WLAN_EID_S1G_CAPABILITIES: 10688c2ecf20Sopenharmony_ci case WLAN_EID_S1G_OPERATION: 10698c2ecf20Sopenharmony_ci case WLAN_EID_AID_RESPONSE: 10708c2ecf20Sopenharmony_ci case WLAN_EID_S1G_SHORT_BCN_INTERVAL: 10718c2ecf20Sopenharmony_ci /* 10728c2ecf20Sopenharmony_ci * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible 10738c2ecf20Sopenharmony_ci * that if the content gets bigger it might be needed more than once 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_ci if (test_bit(id, seen_elems)) { 10768c2ecf20Sopenharmony_ci elems->parse_error = true; 10778c2ecf20Sopenharmony_ci continue; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci break; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (calc_crc && id < 64 && (filter & (1ULL << id))) 10838c2ecf20Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci elem_parse_failed = false; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci switch (id) { 10888c2ecf20Sopenharmony_ci case WLAN_EID_LINK_ID: 10898c2ecf20Sopenharmony_ci if (elen + 2 < sizeof(struct ieee80211_tdls_lnkie)) { 10908c2ecf20Sopenharmony_ci elem_parse_failed = true; 10918c2ecf20Sopenharmony_ci break; 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci elems->lnk_id = (void *)(pos - 2); 10948c2ecf20Sopenharmony_ci break; 10958c2ecf20Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_TIMING: 10968c2ecf20Sopenharmony_ci if (elen < sizeof(struct ieee80211_ch_switch_timing)) { 10978c2ecf20Sopenharmony_ci elem_parse_failed = true; 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci elems->ch_sw_timing = (void *)pos; 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci case WLAN_EID_EXT_CAPABILITY: 11038c2ecf20Sopenharmony_ci elems->ext_capab = pos; 11048c2ecf20Sopenharmony_ci elems->ext_capab_len = elen; 11058c2ecf20Sopenharmony_ci break; 11068c2ecf20Sopenharmony_ci case WLAN_EID_SSID: 11078c2ecf20Sopenharmony_ci elems->ssid = pos; 11088c2ecf20Sopenharmony_ci elems->ssid_len = elen; 11098c2ecf20Sopenharmony_ci break; 11108c2ecf20Sopenharmony_ci case WLAN_EID_SUPP_RATES: 11118c2ecf20Sopenharmony_ci elems->supp_rates = pos; 11128c2ecf20Sopenharmony_ci elems->supp_rates_len = elen; 11138c2ecf20Sopenharmony_ci break; 11148c2ecf20Sopenharmony_ci case WLAN_EID_DS_PARAMS: 11158c2ecf20Sopenharmony_ci if (elen >= 1) 11168c2ecf20Sopenharmony_ci elems->ds_params = pos; 11178c2ecf20Sopenharmony_ci else 11188c2ecf20Sopenharmony_ci elem_parse_failed = true; 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci case WLAN_EID_TIM: 11218c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_tim_ie)) { 11228c2ecf20Sopenharmony_ci elems->tim = (void *)pos; 11238c2ecf20Sopenharmony_ci elems->tim_len = elen; 11248c2ecf20Sopenharmony_ci } else 11258c2ecf20Sopenharmony_ci elem_parse_failed = true; 11268c2ecf20Sopenharmony_ci break; 11278c2ecf20Sopenharmony_ci case WLAN_EID_VENDOR_SPECIFIC: 11288c2ecf20Sopenharmony_ci if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && 11298c2ecf20Sopenharmony_ci pos[2] == 0xf2) { 11308c2ecf20Sopenharmony_ci /* Microsoft OUI (00:50:F2) */ 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (calc_crc) 11338c2ecf20Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (elen >= 5 && pos[3] == 2) { 11368c2ecf20Sopenharmony_ci /* OUI Type 2 - WMM IE */ 11378c2ecf20Sopenharmony_ci if (pos[4] == 0) { 11388c2ecf20Sopenharmony_ci elems->wmm_info = pos; 11398c2ecf20Sopenharmony_ci elems->wmm_info_len = elen; 11408c2ecf20Sopenharmony_ci } else if (pos[4] == 1) { 11418c2ecf20Sopenharmony_ci elems->wmm_param = pos; 11428c2ecf20Sopenharmony_ci elems->wmm_param_len = elen; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci break; 11478c2ecf20Sopenharmony_ci case WLAN_EID_RSN: 11488c2ecf20Sopenharmony_ci elems->rsn = pos; 11498c2ecf20Sopenharmony_ci elems->rsn_len = elen; 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci case WLAN_EID_ERP_INFO: 11528c2ecf20Sopenharmony_ci if (elen >= 1) 11538c2ecf20Sopenharmony_ci elems->erp_info = pos; 11548c2ecf20Sopenharmony_ci else 11558c2ecf20Sopenharmony_ci elem_parse_failed = true; 11568c2ecf20Sopenharmony_ci break; 11578c2ecf20Sopenharmony_ci case WLAN_EID_EXT_SUPP_RATES: 11588c2ecf20Sopenharmony_ci elems->ext_supp_rates = pos; 11598c2ecf20Sopenharmony_ci elems->ext_supp_rates_len = elen; 11608c2ecf20Sopenharmony_ci break; 11618c2ecf20Sopenharmony_ci case WLAN_EID_HT_CAPABILITY: 11628c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_ht_cap)) 11638c2ecf20Sopenharmony_ci elems->ht_cap_elem = (void *)pos; 11648c2ecf20Sopenharmony_ci else 11658c2ecf20Sopenharmony_ci elem_parse_failed = true; 11668c2ecf20Sopenharmony_ci break; 11678c2ecf20Sopenharmony_ci case WLAN_EID_HT_OPERATION: 11688c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_ht_operation)) 11698c2ecf20Sopenharmony_ci elems->ht_operation = (void *)pos; 11708c2ecf20Sopenharmony_ci else 11718c2ecf20Sopenharmony_ci elem_parse_failed = true; 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci case WLAN_EID_VHT_CAPABILITY: 11748c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_vht_cap)) 11758c2ecf20Sopenharmony_ci elems->vht_cap_elem = (void *)pos; 11768c2ecf20Sopenharmony_ci else 11778c2ecf20Sopenharmony_ci elem_parse_failed = true; 11788c2ecf20Sopenharmony_ci break; 11798c2ecf20Sopenharmony_ci case WLAN_EID_VHT_OPERATION: 11808c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_vht_operation)) { 11818c2ecf20Sopenharmony_ci elems->vht_operation = (void *)pos; 11828c2ecf20Sopenharmony_ci if (calc_crc) 11838c2ecf20Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 11848c2ecf20Sopenharmony_ci break; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci elem_parse_failed = true; 11878c2ecf20Sopenharmony_ci break; 11888c2ecf20Sopenharmony_ci case WLAN_EID_OPMODE_NOTIF: 11898c2ecf20Sopenharmony_ci if (elen > 0) { 11908c2ecf20Sopenharmony_ci elems->opmode_notif = pos; 11918c2ecf20Sopenharmony_ci if (calc_crc) 11928c2ecf20Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 11938c2ecf20Sopenharmony_ci break; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci elem_parse_failed = true; 11968c2ecf20Sopenharmony_ci break; 11978c2ecf20Sopenharmony_ci case WLAN_EID_MESH_ID: 11988c2ecf20Sopenharmony_ci elems->mesh_id = pos; 11998c2ecf20Sopenharmony_ci elems->mesh_id_len = elen; 12008c2ecf20Sopenharmony_ci break; 12018c2ecf20Sopenharmony_ci case WLAN_EID_MESH_CONFIG: 12028c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_meshconf_ie)) 12038c2ecf20Sopenharmony_ci elems->mesh_config = (void *)pos; 12048c2ecf20Sopenharmony_ci else 12058c2ecf20Sopenharmony_ci elem_parse_failed = true; 12068c2ecf20Sopenharmony_ci break; 12078c2ecf20Sopenharmony_ci case WLAN_EID_PEER_MGMT: 12088c2ecf20Sopenharmony_ci elems->peering = pos; 12098c2ecf20Sopenharmony_ci elems->peering_len = elen; 12108c2ecf20Sopenharmony_ci break; 12118c2ecf20Sopenharmony_ci case WLAN_EID_MESH_AWAKE_WINDOW: 12128c2ecf20Sopenharmony_ci if (elen >= 2) 12138c2ecf20Sopenharmony_ci elems->awake_window = (void *)pos; 12148c2ecf20Sopenharmony_ci break; 12158c2ecf20Sopenharmony_ci case WLAN_EID_PREQ: 12168c2ecf20Sopenharmony_ci elems->preq = pos; 12178c2ecf20Sopenharmony_ci elems->preq_len = elen; 12188c2ecf20Sopenharmony_ci break; 12198c2ecf20Sopenharmony_ci case WLAN_EID_PREP: 12208c2ecf20Sopenharmony_ci elems->prep = pos; 12218c2ecf20Sopenharmony_ci elems->prep_len = elen; 12228c2ecf20Sopenharmony_ci break; 12238c2ecf20Sopenharmony_ci case WLAN_EID_PERR: 12248c2ecf20Sopenharmony_ci elems->perr = pos; 12258c2ecf20Sopenharmony_ci elems->perr_len = elen; 12268c2ecf20Sopenharmony_ci break; 12278c2ecf20Sopenharmony_ci case WLAN_EID_RANN: 12288c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_rann_ie)) 12298c2ecf20Sopenharmony_ci elems->rann = (void *)pos; 12308c2ecf20Sopenharmony_ci else 12318c2ecf20Sopenharmony_ci elem_parse_failed = true; 12328c2ecf20Sopenharmony_ci break; 12338c2ecf20Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH: 12348c2ecf20Sopenharmony_ci if (elen != sizeof(struct ieee80211_channel_sw_ie)) { 12358c2ecf20Sopenharmony_ci elem_parse_failed = true; 12368c2ecf20Sopenharmony_ci break; 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci elems->ch_switch_ie = (void *)pos; 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci case WLAN_EID_EXT_CHANSWITCH_ANN: 12418c2ecf20Sopenharmony_ci if (elen != sizeof(struct ieee80211_ext_chansw_ie)) { 12428c2ecf20Sopenharmony_ci elem_parse_failed = true; 12438c2ecf20Sopenharmony_ci break; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci elems->ext_chansw_ie = (void *)pos; 12468c2ecf20Sopenharmony_ci break; 12478c2ecf20Sopenharmony_ci case WLAN_EID_SECONDARY_CHANNEL_OFFSET: 12488c2ecf20Sopenharmony_ci if (elen != sizeof(struct ieee80211_sec_chan_offs_ie)) { 12498c2ecf20Sopenharmony_ci elem_parse_failed = true; 12508c2ecf20Sopenharmony_ci break; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci elems->sec_chan_offs = (void *)pos; 12538c2ecf20Sopenharmony_ci break; 12548c2ecf20Sopenharmony_ci case WLAN_EID_CHAN_SWITCH_PARAM: 12558c2ecf20Sopenharmony_ci if (elen < 12568c2ecf20Sopenharmony_ci sizeof(*elems->mesh_chansw_params_ie)) { 12578c2ecf20Sopenharmony_ci elem_parse_failed = true; 12588c2ecf20Sopenharmony_ci break; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci elems->mesh_chansw_params_ie = (void *)pos; 12618c2ecf20Sopenharmony_ci break; 12628c2ecf20Sopenharmony_ci case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: 12638c2ecf20Sopenharmony_ci if (!action || 12648c2ecf20Sopenharmony_ci elen < sizeof(*elems->wide_bw_chansw_ie)) { 12658c2ecf20Sopenharmony_ci elem_parse_failed = true; 12668c2ecf20Sopenharmony_ci break; 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci elems->wide_bw_chansw_ie = (void *)pos; 12698c2ecf20Sopenharmony_ci break; 12708c2ecf20Sopenharmony_ci case WLAN_EID_CHANNEL_SWITCH_WRAPPER: 12718c2ecf20Sopenharmony_ci if (action) { 12728c2ecf20Sopenharmony_ci elem_parse_failed = true; 12738c2ecf20Sopenharmony_ci break; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci /* 12768c2ecf20Sopenharmony_ci * This is a bit tricky, but as we only care about 12778c2ecf20Sopenharmony_ci * the wide bandwidth channel switch element, so 12788c2ecf20Sopenharmony_ci * just parse it out manually. 12798c2ecf20Sopenharmony_ci */ 12808c2ecf20Sopenharmony_ci ie = cfg80211_find_ie(WLAN_EID_WIDE_BW_CHANNEL_SWITCH, 12818c2ecf20Sopenharmony_ci pos, elen); 12828c2ecf20Sopenharmony_ci if (ie) { 12838c2ecf20Sopenharmony_ci if (ie[1] >= sizeof(*elems->wide_bw_chansw_ie)) 12848c2ecf20Sopenharmony_ci elems->wide_bw_chansw_ie = 12858c2ecf20Sopenharmony_ci (void *)(ie + 2); 12868c2ecf20Sopenharmony_ci else 12878c2ecf20Sopenharmony_ci elem_parse_failed = true; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci break; 12908c2ecf20Sopenharmony_ci case WLAN_EID_COUNTRY: 12918c2ecf20Sopenharmony_ci elems->country_elem = pos; 12928c2ecf20Sopenharmony_ci elems->country_elem_len = elen; 12938c2ecf20Sopenharmony_ci break; 12948c2ecf20Sopenharmony_ci case WLAN_EID_PWR_CONSTRAINT: 12958c2ecf20Sopenharmony_ci if (elen != 1) { 12968c2ecf20Sopenharmony_ci elem_parse_failed = true; 12978c2ecf20Sopenharmony_ci break; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci elems->pwr_constr_elem = pos; 13008c2ecf20Sopenharmony_ci break; 13018c2ecf20Sopenharmony_ci case WLAN_EID_CISCO_VENDOR_SPECIFIC: 13028c2ecf20Sopenharmony_ci /* Lots of different options exist, but we only care 13038c2ecf20Sopenharmony_ci * about the Dynamic Transmit Power Control element. 13048c2ecf20Sopenharmony_ci * First check for the Cisco OUI, then for the DTPC 13058c2ecf20Sopenharmony_ci * tag (0x00). 13068c2ecf20Sopenharmony_ci */ 13078c2ecf20Sopenharmony_ci if (elen < 4) { 13088c2ecf20Sopenharmony_ci elem_parse_failed = true; 13098c2ecf20Sopenharmony_ci break; 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (pos[0] != 0x00 || pos[1] != 0x40 || 13138c2ecf20Sopenharmony_ci pos[2] != 0x96 || pos[3] != 0x00) 13148c2ecf20Sopenharmony_ci break; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (elen != 6) { 13178c2ecf20Sopenharmony_ci elem_parse_failed = true; 13188c2ecf20Sopenharmony_ci break; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci if (calc_crc) 13228c2ecf20Sopenharmony_ci crc = crc32_be(crc, pos - 2, elen + 2); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci elems->cisco_dtpc_elem = pos; 13258c2ecf20Sopenharmony_ci break; 13268c2ecf20Sopenharmony_ci case WLAN_EID_ADDBA_EXT: 13278c2ecf20Sopenharmony_ci if (elen < sizeof(struct ieee80211_addba_ext_ie)) { 13288c2ecf20Sopenharmony_ci elem_parse_failed = true; 13298c2ecf20Sopenharmony_ci break; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci elems->addba_ext_ie = (void *)pos; 13328c2ecf20Sopenharmony_ci break; 13338c2ecf20Sopenharmony_ci case WLAN_EID_TIMEOUT_INTERVAL: 13348c2ecf20Sopenharmony_ci if (elen >= sizeof(struct ieee80211_timeout_interval_ie)) 13358c2ecf20Sopenharmony_ci elems->timeout_int = (void *)pos; 13368c2ecf20Sopenharmony_ci else 13378c2ecf20Sopenharmony_ci elem_parse_failed = true; 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci case WLAN_EID_BSS_MAX_IDLE_PERIOD: 13408c2ecf20Sopenharmony_ci if (elen >= sizeof(*elems->max_idle_period_ie)) 13418c2ecf20Sopenharmony_ci elems->max_idle_period_ie = (void *)pos; 13428c2ecf20Sopenharmony_ci break; 13438c2ecf20Sopenharmony_ci case WLAN_EID_RSNX: 13448c2ecf20Sopenharmony_ci elems->rsnx = pos; 13458c2ecf20Sopenharmony_ci elems->rsnx_len = elen; 13468c2ecf20Sopenharmony_ci break; 13478c2ecf20Sopenharmony_ci case WLAN_EID_EXTENSION: 13488c2ecf20Sopenharmony_ci ieee80211_parse_extension_element(calc_crc ? 13498c2ecf20Sopenharmony_ci &crc : NULL, 13508c2ecf20Sopenharmony_ci elem, elems); 13518c2ecf20Sopenharmony_ci break; 13528c2ecf20Sopenharmony_ci case WLAN_EID_S1G_CAPABILITIES: 13538c2ecf20Sopenharmony_ci if (elen >= sizeof(*elems->s1g_capab)) 13548c2ecf20Sopenharmony_ci elems->s1g_capab = (void *)pos; 13558c2ecf20Sopenharmony_ci else 13568c2ecf20Sopenharmony_ci elem_parse_failed = true; 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci case WLAN_EID_S1G_OPERATION: 13598c2ecf20Sopenharmony_ci if (elen == sizeof(*elems->s1g_oper)) 13608c2ecf20Sopenharmony_ci elems->s1g_oper = (void *)pos; 13618c2ecf20Sopenharmony_ci else 13628c2ecf20Sopenharmony_ci elem_parse_failed = true; 13638c2ecf20Sopenharmony_ci break; 13648c2ecf20Sopenharmony_ci case WLAN_EID_S1G_BCN_COMPAT: 13658c2ecf20Sopenharmony_ci if (elen == sizeof(*elems->s1g_bcn_compat)) 13668c2ecf20Sopenharmony_ci elems->s1g_bcn_compat = (void *)pos; 13678c2ecf20Sopenharmony_ci else 13688c2ecf20Sopenharmony_ci elem_parse_failed = true; 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci case WLAN_EID_AID_RESPONSE: 13718c2ecf20Sopenharmony_ci if (elen == sizeof(struct ieee80211_aid_response_ie)) 13728c2ecf20Sopenharmony_ci elems->aid_resp = (void *)pos; 13738c2ecf20Sopenharmony_ci else 13748c2ecf20Sopenharmony_ci elem_parse_failed = true; 13758c2ecf20Sopenharmony_ci break; 13768c2ecf20Sopenharmony_ci default: 13778c2ecf20Sopenharmony_ci break; 13788c2ecf20Sopenharmony_ci } 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci if (elem_parse_failed) 13818c2ecf20Sopenharmony_ci elems->parse_error = true; 13828c2ecf20Sopenharmony_ci else 13838c2ecf20Sopenharmony_ci __set_bit(id, seen_elems); 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (!for_each_element_completed(elem, start, len)) 13878c2ecf20Sopenharmony_ci elems->parse_error = true; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci return crc; 13908c2ecf20Sopenharmony_ci} 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_cistatic size_t ieee802_11_find_bssid_profile(const u8 *start, size_t len, 13938c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 13948c2ecf20Sopenharmony_ci u8 *transmitter_bssid, 13958c2ecf20Sopenharmony_ci u8 *bss_bssid, 13968c2ecf20Sopenharmony_ci u8 *nontransmitted_profile) 13978c2ecf20Sopenharmony_ci{ 13988c2ecf20Sopenharmony_ci const struct element *elem, *sub; 13998c2ecf20Sopenharmony_ci size_t profile_len = 0; 14008c2ecf20Sopenharmony_ci bool found = false; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (!bss_bssid || !transmitter_bssid) 14038c2ecf20Sopenharmony_ci return profile_len; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, start, len) { 14068c2ecf20Sopenharmony_ci if (elem->datalen < 2) 14078c2ecf20Sopenharmony_ci continue; 14088c2ecf20Sopenharmony_ci if (elem->data[0] < 1 || elem->data[0] > 8) 14098c2ecf20Sopenharmony_ci continue; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci for_each_element(sub, elem->data + 1, elem->datalen - 1) { 14128c2ecf20Sopenharmony_ci u8 new_bssid[ETH_ALEN]; 14138c2ecf20Sopenharmony_ci const u8 *index; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (sub->id != 0 || sub->datalen < 4) { 14168c2ecf20Sopenharmony_ci /* not a valid BSS profile */ 14178c2ecf20Sopenharmony_ci continue; 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP || 14218c2ecf20Sopenharmony_ci sub->data[1] != 2) { 14228c2ecf20Sopenharmony_ci /* The first element of the 14238c2ecf20Sopenharmony_ci * Nontransmitted BSSID Profile is not 14248c2ecf20Sopenharmony_ci * the Nontransmitted BSSID Capability 14258c2ecf20Sopenharmony_ci * element. 14268c2ecf20Sopenharmony_ci */ 14278c2ecf20Sopenharmony_ci continue; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci memset(nontransmitted_profile, 0, len); 14318c2ecf20Sopenharmony_ci profile_len = cfg80211_merge_profile(start, len, 14328c2ecf20Sopenharmony_ci elem, 14338c2ecf20Sopenharmony_ci sub, 14348c2ecf20Sopenharmony_ci nontransmitted_profile, 14358c2ecf20Sopenharmony_ci len); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* found a Nontransmitted BSSID Profile */ 14388c2ecf20Sopenharmony_ci index = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, 14398c2ecf20Sopenharmony_ci nontransmitted_profile, 14408c2ecf20Sopenharmony_ci profile_len); 14418c2ecf20Sopenharmony_ci if (!index || index[1] < 1 || index[2] == 0) { 14428c2ecf20Sopenharmony_ci /* Invalid MBSSID Index element */ 14438c2ecf20Sopenharmony_ci continue; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci cfg80211_gen_new_bssid(transmitter_bssid, 14478c2ecf20Sopenharmony_ci elem->data[0], 14488c2ecf20Sopenharmony_ci index[2], 14498c2ecf20Sopenharmony_ci new_bssid); 14508c2ecf20Sopenharmony_ci if (ether_addr_equal(new_bssid, bss_bssid)) { 14518c2ecf20Sopenharmony_ci found = true; 14528c2ecf20Sopenharmony_ci elems->bssid_index_len = index[1]; 14538c2ecf20Sopenharmony_ci elems->bssid_index = (void *)&index[2]; 14548c2ecf20Sopenharmony_ci break; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci return found ? profile_len : 0; 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ciu32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, 14638c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 14648c2ecf20Sopenharmony_ci u64 filter, u32 crc, u8 *transmitter_bssid, 14658c2ecf20Sopenharmony_ci u8 *bss_bssid) 14668c2ecf20Sopenharmony_ci{ 14678c2ecf20Sopenharmony_ci const struct element *non_inherit = NULL; 14688c2ecf20Sopenharmony_ci u8 *nontransmitted_profile; 14698c2ecf20Sopenharmony_ci int nontransmitted_profile_len = 0; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci memset(elems, 0, sizeof(*elems)); 14728c2ecf20Sopenharmony_ci elems->ie_start = start; 14738c2ecf20Sopenharmony_ci elems->total_len = len; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci nontransmitted_profile = kmalloc(len, GFP_ATOMIC); 14768c2ecf20Sopenharmony_ci if (nontransmitted_profile) { 14778c2ecf20Sopenharmony_ci nontransmitted_profile_len = 14788c2ecf20Sopenharmony_ci ieee802_11_find_bssid_profile(start, len, elems, 14798c2ecf20Sopenharmony_ci transmitter_bssid, 14808c2ecf20Sopenharmony_ci bss_bssid, 14818c2ecf20Sopenharmony_ci nontransmitted_profile); 14828c2ecf20Sopenharmony_ci non_inherit = 14838c2ecf20Sopenharmony_ci cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, 14848c2ecf20Sopenharmony_ci nontransmitted_profile, 14858c2ecf20Sopenharmony_ci nontransmitted_profile_len); 14868c2ecf20Sopenharmony_ci if (!nontransmitted_profile_len) { 14878c2ecf20Sopenharmony_ci nontransmitted_profile_len = 0; 14888c2ecf20Sopenharmony_ci kfree(nontransmitted_profile); 14898c2ecf20Sopenharmony_ci nontransmitted_profile = NULL; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci crc = _ieee802_11_parse_elems_crc(start, len, action, elems, filter, 14948c2ecf20Sopenharmony_ci crc, non_inherit); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* Override with nontransmitted profile, if found */ 14978c2ecf20Sopenharmony_ci if (nontransmitted_profile_len) 14988c2ecf20Sopenharmony_ci _ieee802_11_parse_elems_crc(nontransmitted_profile, 14998c2ecf20Sopenharmony_ci nontransmitted_profile_len, 15008c2ecf20Sopenharmony_ci action, elems, 0, 0, NULL); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (elems->tim && !elems->parse_error) { 15038c2ecf20Sopenharmony_ci const struct ieee80211_tim_ie *tim_ie = elems->tim; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci elems->dtim_period = tim_ie->dtim_period; 15068c2ecf20Sopenharmony_ci elems->dtim_count = tim_ie->dtim_count; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci /* Override DTIM period and count if needed */ 15108c2ecf20Sopenharmony_ci if (elems->bssid_index && 15118c2ecf20Sopenharmony_ci elems->bssid_index_len >= 15128c2ecf20Sopenharmony_ci offsetofend(struct ieee80211_bssid_index, dtim_period)) 15138c2ecf20Sopenharmony_ci elems->dtim_period = elems->bssid_index->dtim_period; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (elems->bssid_index && 15168c2ecf20Sopenharmony_ci elems->bssid_index_len >= 15178c2ecf20Sopenharmony_ci offsetofend(struct ieee80211_bssid_index, dtim_count)) 15188c2ecf20Sopenharmony_ci elems->dtim_count = elems->bssid_index->dtim_count; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci elems->nontx_profile = nontransmitted_profile; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci return crc; 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_civoid ieee80211_regulatory_limit_wmm_params(struct ieee80211_sub_if_data *sdata, 15268c2ecf20Sopenharmony_ci struct ieee80211_tx_queue_params 15278c2ecf20Sopenharmony_ci *qparam, int ac) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 15308c2ecf20Sopenharmony_ci const struct ieee80211_reg_rule *rrule; 15318c2ecf20Sopenharmony_ci const struct ieee80211_wmm_ac *wmm_ac; 15328c2ecf20Sopenharmony_ci u16 center_freq = 0; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_AP && 15358c2ecf20Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_STATION) 15368c2ecf20Sopenharmony_ci return; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci rcu_read_lock(); 15398c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 15408c2ecf20Sopenharmony_ci if (chanctx_conf) 15418c2ecf20Sopenharmony_ci center_freq = chanctx_conf->def.chan->center_freq; 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci if (!center_freq) { 15448c2ecf20Sopenharmony_ci rcu_read_unlock(); 15458c2ecf20Sopenharmony_ci return; 15468c2ecf20Sopenharmony_ci } 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci rrule = freq_reg_info(sdata->wdev.wiphy, MHZ_TO_KHZ(center_freq)); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(rrule) || !rrule->has_wmm) { 15518c2ecf20Sopenharmony_ci rcu_read_unlock(); 15528c2ecf20Sopenharmony_ci return; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) 15568c2ecf20Sopenharmony_ci wmm_ac = &rrule->wmm_rule.ap[ac]; 15578c2ecf20Sopenharmony_ci else 15588c2ecf20Sopenharmony_ci wmm_ac = &rrule->wmm_rule.client[ac]; 15598c2ecf20Sopenharmony_ci qparam->cw_min = max_t(u16, qparam->cw_min, wmm_ac->cw_min); 15608c2ecf20Sopenharmony_ci qparam->cw_max = max_t(u16, qparam->cw_max, wmm_ac->cw_max); 15618c2ecf20Sopenharmony_ci qparam->aifs = max_t(u8, qparam->aifs, wmm_ac->aifsn); 15628c2ecf20Sopenharmony_ci qparam->txop = min_t(u16, qparam->txop, wmm_ac->cot / 32); 15638c2ecf20Sopenharmony_ci rcu_read_unlock(); 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_civoid ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, 15678c2ecf20Sopenharmony_ci bool bss_notify, bool enable_qos) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 15708c2ecf20Sopenharmony_ci struct ieee80211_tx_queue_params qparam; 15718c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 15728c2ecf20Sopenharmony_ci int ac; 15738c2ecf20Sopenharmony_ci bool use_11b; 15748c2ecf20Sopenharmony_ci bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */ 15758c2ecf20Sopenharmony_ci int aCWmin, aCWmax; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (!local->ops->conf_tx) 15788c2ecf20Sopenharmony_ci return; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (local->hw.queues < IEEE80211_NUM_ACS) 15818c2ecf20Sopenharmony_ci return; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci memset(&qparam, 0, sizeof(qparam)); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci rcu_read_lock(); 15868c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 15878c2ecf20Sopenharmony_ci use_11b = (chanctx_conf && 15888c2ecf20Sopenharmony_ci chanctx_conf->def.chan->band == NL80211_BAND_2GHZ) && 15898c2ecf20Sopenharmony_ci !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); 15908c2ecf20Sopenharmony_ci rcu_read_unlock(); 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci /* Set defaults according to 802.11-2007 Table 7-37 */ 15958c2ecf20Sopenharmony_ci aCWmax = 1023; 15968c2ecf20Sopenharmony_ci if (use_11b) 15978c2ecf20Sopenharmony_ci aCWmin = 31; 15988c2ecf20Sopenharmony_ci else 15998c2ecf20Sopenharmony_ci aCWmin = 15; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci /* Confiure old 802.11b/g medium access rules. */ 16028c2ecf20Sopenharmony_ci qparam.cw_max = aCWmax; 16038c2ecf20Sopenharmony_ci qparam.cw_min = aCWmin; 16048c2ecf20Sopenharmony_ci qparam.txop = 0; 16058c2ecf20Sopenharmony_ci qparam.aifs = 2; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 16088c2ecf20Sopenharmony_ci /* Update if QoS is enabled. */ 16098c2ecf20Sopenharmony_ci if (enable_qos) { 16108c2ecf20Sopenharmony_ci switch (ac) { 16118c2ecf20Sopenharmony_ci case IEEE80211_AC_BK: 16128c2ecf20Sopenharmony_ci qparam.cw_max = aCWmax; 16138c2ecf20Sopenharmony_ci qparam.cw_min = aCWmin; 16148c2ecf20Sopenharmony_ci qparam.txop = 0; 16158c2ecf20Sopenharmony_ci if (is_ocb) 16168c2ecf20Sopenharmony_ci qparam.aifs = 9; 16178c2ecf20Sopenharmony_ci else 16188c2ecf20Sopenharmony_ci qparam.aifs = 7; 16198c2ecf20Sopenharmony_ci break; 16208c2ecf20Sopenharmony_ci /* never happens but let's not leave undefined */ 16218c2ecf20Sopenharmony_ci default: 16228c2ecf20Sopenharmony_ci case IEEE80211_AC_BE: 16238c2ecf20Sopenharmony_ci qparam.cw_max = aCWmax; 16248c2ecf20Sopenharmony_ci qparam.cw_min = aCWmin; 16258c2ecf20Sopenharmony_ci qparam.txop = 0; 16268c2ecf20Sopenharmony_ci if (is_ocb) 16278c2ecf20Sopenharmony_ci qparam.aifs = 6; 16288c2ecf20Sopenharmony_ci else 16298c2ecf20Sopenharmony_ci qparam.aifs = 3; 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci case IEEE80211_AC_VI: 16328c2ecf20Sopenharmony_ci qparam.cw_max = aCWmin; 16338c2ecf20Sopenharmony_ci qparam.cw_min = (aCWmin + 1) / 2 - 1; 16348c2ecf20Sopenharmony_ci if (is_ocb) 16358c2ecf20Sopenharmony_ci qparam.txop = 0; 16368c2ecf20Sopenharmony_ci else if (use_11b) 16378c2ecf20Sopenharmony_ci qparam.txop = 6016/32; 16388c2ecf20Sopenharmony_ci else 16398c2ecf20Sopenharmony_ci qparam.txop = 3008/32; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (is_ocb) 16428c2ecf20Sopenharmony_ci qparam.aifs = 3; 16438c2ecf20Sopenharmony_ci else 16448c2ecf20Sopenharmony_ci qparam.aifs = 2; 16458c2ecf20Sopenharmony_ci break; 16468c2ecf20Sopenharmony_ci case IEEE80211_AC_VO: 16478c2ecf20Sopenharmony_ci qparam.cw_max = (aCWmin + 1) / 2 - 1; 16488c2ecf20Sopenharmony_ci qparam.cw_min = (aCWmin + 1) / 4 - 1; 16498c2ecf20Sopenharmony_ci if (is_ocb) 16508c2ecf20Sopenharmony_ci qparam.txop = 0; 16518c2ecf20Sopenharmony_ci else if (use_11b) 16528c2ecf20Sopenharmony_ci qparam.txop = 3264/32; 16538c2ecf20Sopenharmony_ci else 16548c2ecf20Sopenharmony_ci qparam.txop = 1504/32; 16558c2ecf20Sopenharmony_ci qparam.aifs = 2; 16568c2ecf20Sopenharmony_ci break; 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci ieee80211_regulatory_limit_wmm_params(sdata, &qparam, ac); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci qparam.uapsd = false; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci sdata->tx_conf[ac] = qparam; 16648c2ecf20Sopenharmony_ci drv_conf_tx(local, sdata, ac, &qparam); 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_MONITOR && 16688c2ecf20Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && 16698c2ecf20Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_NAN) { 16708c2ecf20Sopenharmony_ci sdata->vif.bss_conf.qos = enable_qos; 16718c2ecf20Sopenharmony_ci if (bss_notify) 16728c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, 16738c2ecf20Sopenharmony_ci BSS_CHANGED_QOS); 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_civoid ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, 16788c2ecf20Sopenharmony_ci u16 transaction, u16 auth_alg, u16 status, 16798c2ecf20Sopenharmony_ci const u8 *extra, size_t extra_len, const u8 *da, 16808c2ecf20Sopenharmony_ci const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx, 16818c2ecf20Sopenharmony_ci u32 tx_flags) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 16848c2ecf20Sopenharmony_ci struct sk_buff *skb; 16858c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 16868c2ecf20Sopenharmony_ci int err; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* 24 + 6 = header + auth_algo + auth_transaction + status_code */ 16898c2ecf20Sopenharmony_ci skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN + 16908c2ecf20Sopenharmony_ci 24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN); 16918c2ecf20Sopenharmony_ci if (!skb) 16928c2ecf20Sopenharmony_ci return; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci mgmt = skb_put_zero(skb, 24 + 6); 16978c2ecf20Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 16988c2ecf20Sopenharmony_ci IEEE80211_STYPE_AUTH); 16998c2ecf20Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 17008c2ecf20Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 17018c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, bssid, ETH_ALEN); 17028c2ecf20Sopenharmony_ci mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); 17038c2ecf20Sopenharmony_ci mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); 17048c2ecf20Sopenharmony_ci mgmt->u.auth.status_code = cpu_to_le16(status); 17058c2ecf20Sopenharmony_ci if (extra) 17068c2ecf20Sopenharmony_ci skb_put_data(skb, extra, extra_len); 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) { 17098c2ecf20Sopenharmony_ci mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); 17108c2ecf20Sopenharmony_ci err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx); 17118c2ecf20Sopenharmony_ci WARN_ON(err); 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT | 17158c2ecf20Sopenharmony_ci tx_flags; 17168c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_civoid ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, 17208c2ecf20Sopenharmony_ci const u8 *da, const u8 *bssid, 17218c2ecf20Sopenharmony_ci u16 stype, u16 reason, 17228c2ecf20Sopenharmony_ci bool send_frame, u8 *frame_buf) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 17258c2ecf20Sopenharmony_ci struct sk_buff *skb; 17268c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt = (void *)frame_buf; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci /* build frame */ 17298c2ecf20Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype); 17308c2ecf20Sopenharmony_ci mgmt->duration = 0; /* initialize only */ 17318c2ecf20Sopenharmony_ci mgmt->seq_ctrl = 0; /* initialize only */ 17328c2ecf20Sopenharmony_ci memcpy(mgmt->da, da, ETH_ALEN); 17338c2ecf20Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 17348c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, bssid, ETH_ALEN); 17358c2ecf20Sopenharmony_ci /* u.deauth.reason_code == u.disassoc.reason_code */ 17368c2ecf20Sopenharmony_ci mgmt->u.deauth.reason_code = cpu_to_le16(reason); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci if (send_frame) { 17398c2ecf20Sopenharmony_ci skb = dev_alloc_skb(local->hw.extra_tx_headroom + 17408c2ecf20Sopenharmony_ci IEEE80211_DEAUTH_FRAME_LEN); 17418c2ecf20Sopenharmony_ci if (!skb) 17428c2ecf20Sopenharmony_ci return; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci skb_reserve(skb, local->hw.extra_tx_headroom); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* copy in frame */ 17478c2ecf20Sopenharmony_ci skb_put_data(skb, mgmt, IEEE80211_DEAUTH_FRAME_LEN); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION || 17508c2ecf20Sopenharmony_ci !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED)) 17518c2ecf20Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= 17528c2ecf20Sopenharmony_ci IEEE80211_TX_INTFL_DONT_ENCRYPT; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci} 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_cistatic u8 *ieee80211_write_he_6ghz_cap(u8 *pos, __le16 cap, u8 *end) 17598c2ecf20Sopenharmony_ci{ 17608c2ecf20Sopenharmony_ci if ((end - pos) < 5) 17618c2ecf20Sopenharmony_ci return pos; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 17648c2ecf20Sopenharmony_ci *pos++ = 1 + sizeof(cap); 17658c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXT_HE_6GHZ_CAPA; 17668c2ecf20Sopenharmony_ci memcpy(pos, &cap, sizeof(cap)); 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci return pos + 2; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_cistatic int ieee80211_build_preq_ies_band(struct ieee80211_sub_if_data *sdata, 17728c2ecf20Sopenharmony_ci u8 *buffer, size_t buffer_len, 17738c2ecf20Sopenharmony_ci const u8 *ie, size_t ie_len, 17748c2ecf20Sopenharmony_ci enum nl80211_band band, 17758c2ecf20Sopenharmony_ci u32 rate_mask, 17768c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef, 17778c2ecf20Sopenharmony_ci size_t *offset, u32 flags) 17788c2ecf20Sopenharmony_ci{ 17798c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 17808c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 17818c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 17828c2ecf20Sopenharmony_ci u8 *pos = buffer, *end = buffer + buffer_len; 17838c2ecf20Sopenharmony_ci size_t noffset; 17848c2ecf20Sopenharmony_ci int supp_rates_len, i; 17858c2ecf20Sopenharmony_ci u8 rates[32]; 17868c2ecf20Sopenharmony_ci int num_rates; 17878c2ecf20Sopenharmony_ci int ext_rates_len; 17888c2ecf20Sopenharmony_ci int shift; 17898c2ecf20Sopenharmony_ci u32 rate_flags; 17908c2ecf20Sopenharmony_ci bool have_80mhz = false; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci *offset = 0; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 17958c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!sband)) 17968c2ecf20Sopenharmony_ci return 0; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(chandef); 17998c2ecf20Sopenharmony_ci shift = ieee80211_chandef_get_shift(chandef); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci num_rates = 0; 18028c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 18038c2ecf20Sopenharmony_ci if ((BIT(i) & rate_mask) == 0) 18048c2ecf20Sopenharmony_ci continue; /* skip rate */ 18058c2ecf20Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 18068c2ecf20Sopenharmony_ci continue; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci rates[num_rates++] = 18098c2ecf20Sopenharmony_ci (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate, 18108c2ecf20Sopenharmony_ci (1 << shift) * 5); 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci supp_rates_len = min_t(int, num_rates, 8); 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (end - pos < 2 + supp_rates_len) 18168c2ecf20Sopenharmony_ci goto out_err; 18178c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_SUPP_RATES; 18188c2ecf20Sopenharmony_ci *pos++ = supp_rates_len; 18198c2ecf20Sopenharmony_ci memcpy(pos, rates, supp_rates_len); 18208c2ecf20Sopenharmony_ci pos += supp_rates_len; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* insert "request information" if in custom IEs */ 18238c2ecf20Sopenharmony_ci if (ie && ie_len) { 18248c2ecf20Sopenharmony_ci static const u8 before_extrates[] = { 18258c2ecf20Sopenharmony_ci WLAN_EID_SSID, 18268c2ecf20Sopenharmony_ci WLAN_EID_SUPP_RATES, 18278c2ecf20Sopenharmony_ci WLAN_EID_REQUEST, 18288c2ecf20Sopenharmony_ci }; 18298c2ecf20Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 18308c2ecf20Sopenharmony_ci before_extrates, 18318c2ecf20Sopenharmony_ci ARRAY_SIZE(before_extrates), 18328c2ecf20Sopenharmony_ci *offset); 18338c2ecf20Sopenharmony_ci if (end - pos < noffset - *offset) 18348c2ecf20Sopenharmony_ci goto out_err; 18358c2ecf20Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 18368c2ecf20Sopenharmony_ci pos += noffset - *offset; 18378c2ecf20Sopenharmony_ci *offset = noffset; 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci ext_rates_len = num_rates - supp_rates_len; 18418c2ecf20Sopenharmony_ci if (ext_rates_len > 0) { 18428c2ecf20Sopenharmony_ci if (end - pos < 2 + ext_rates_len) 18438c2ecf20Sopenharmony_ci goto out_err; 18448c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXT_SUPP_RATES; 18458c2ecf20Sopenharmony_ci *pos++ = ext_rates_len; 18468c2ecf20Sopenharmony_ci memcpy(pos, rates + supp_rates_len, ext_rates_len); 18478c2ecf20Sopenharmony_ci pos += ext_rates_len; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (chandef->chan && sband->band == NL80211_BAND_2GHZ) { 18518c2ecf20Sopenharmony_ci if (end - pos < 3) 18528c2ecf20Sopenharmony_ci goto out_err; 18538c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_DS_PARAMS; 18548c2ecf20Sopenharmony_ci *pos++ = 1; 18558c2ecf20Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel( 18568c2ecf20Sopenharmony_ci chandef->chan->center_freq); 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci if (flags & IEEE80211_PROBE_FLAG_MIN_CONTENT) 18608c2ecf20Sopenharmony_ci goto done; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci /* insert custom IEs that go before HT */ 18638c2ecf20Sopenharmony_ci if (ie && ie_len) { 18648c2ecf20Sopenharmony_ci static const u8 before_ht[] = { 18658c2ecf20Sopenharmony_ci /* 18668c2ecf20Sopenharmony_ci * no need to list the ones split off already 18678c2ecf20Sopenharmony_ci * (or generated here) 18688c2ecf20Sopenharmony_ci */ 18698c2ecf20Sopenharmony_ci WLAN_EID_DS_PARAMS, 18708c2ecf20Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 18718c2ecf20Sopenharmony_ci }; 18728c2ecf20Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 18738c2ecf20Sopenharmony_ci before_ht, ARRAY_SIZE(before_ht), 18748c2ecf20Sopenharmony_ci *offset); 18758c2ecf20Sopenharmony_ci if (end - pos < noffset - *offset) 18768c2ecf20Sopenharmony_ci goto out_err; 18778c2ecf20Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 18788c2ecf20Sopenharmony_ci pos += noffset - *offset; 18798c2ecf20Sopenharmony_ci *offset = noffset; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci if (sband->ht_cap.ht_supported) { 18838c2ecf20Sopenharmony_ci if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) 18848c2ecf20Sopenharmony_ci goto out_err; 18858c2ecf20Sopenharmony_ci pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, 18868c2ecf20Sopenharmony_ci sband->ht_cap.cap); 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci /* insert custom IEs that go before VHT */ 18908c2ecf20Sopenharmony_ci if (ie && ie_len) { 18918c2ecf20Sopenharmony_ci static const u8 before_vht[] = { 18928c2ecf20Sopenharmony_ci /* 18938c2ecf20Sopenharmony_ci * no need to list the ones split off already 18948c2ecf20Sopenharmony_ci * (or generated here) 18958c2ecf20Sopenharmony_ci */ 18968c2ecf20Sopenharmony_ci WLAN_EID_BSS_COEX_2040, 18978c2ecf20Sopenharmony_ci WLAN_EID_EXT_CAPABILITY, 18988c2ecf20Sopenharmony_ci WLAN_EID_SSID_LIST, 18998c2ecf20Sopenharmony_ci WLAN_EID_CHANNEL_USAGE, 19008c2ecf20Sopenharmony_ci WLAN_EID_INTERWORKING, 19018c2ecf20Sopenharmony_ci WLAN_EID_MESH_ID, 19028c2ecf20Sopenharmony_ci /* 60 GHz (Multi-band, DMG, MMS) can't happen */ 19038c2ecf20Sopenharmony_ci }; 19048c2ecf20Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 19058c2ecf20Sopenharmony_ci before_vht, ARRAY_SIZE(before_vht), 19068c2ecf20Sopenharmony_ci *offset); 19078c2ecf20Sopenharmony_ci if (end - pos < noffset - *offset) 19088c2ecf20Sopenharmony_ci goto out_err; 19098c2ecf20Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 19108c2ecf20Sopenharmony_ci pos += noffset - *offset; 19118c2ecf20Sopenharmony_ci *offset = noffset; 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci /* Check if any channel in this sband supports at least 80 MHz */ 19158c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_channels; i++) { 19168c2ecf20Sopenharmony_ci if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | 19178c2ecf20Sopenharmony_ci IEEE80211_CHAN_NO_80MHZ)) 19188c2ecf20Sopenharmony_ci continue; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci have_80mhz = true; 19218c2ecf20Sopenharmony_ci break; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (sband->vht_cap.vht_supported && have_80mhz) { 19258c2ecf20Sopenharmony_ci if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) 19268c2ecf20Sopenharmony_ci goto out_err; 19278c2ecf20Sopenharmony_ci pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, 19288c2ecf20Sopenharmony_ci sband->vht_cap.cap); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci /* insert custom IEs that go before HE */ 19328c2ecf20Sopenharmony_ci if (ie && ie_len) { 19338c2ecf20Sopenharmony_ci static const u8 before_he[] = { 19348c2ecf20Sopenharmony_ci /* 19358c2ecf20Sopenharmony_ci * no need to list the ones split off before VHT 19368c2ecf20Sopenharmony_ci * or generated here 19378c2ecf20Sopenharmony_ci */ 19388c2ecf20Sopenharmony_ci WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS, 19398c2ecf20Sopenharmony_ci WLAN_EID_AP_CSN, 19408c2ecf20Sopenharmony_ci /* TODO: add 11ah/11aj/11ak elements */ 19418c2ecf20Sopenharmony_ci }; 19428c2ecf20Sopenharmony_ci noffset = ieee80211_ie_split(ie, ie_len, 19438c2ecf20Sopenharmony_ci before_he, ARRAY_SIZE(before_he), 19448c2ecf20Sopenharmony_ci *offset); 19458c2ecf20Sopenharmony_ci if (end - pos < noffset - *offset) 19468c2ecf20Sopenharmony_ci goto out_err; 19478c2ecf20Sopenharmony_ci memcpy(pos, ie + *offset, noffset - *offset); 19488c2ecf20Sopenharmony_ci pos += noffset - *offset; 19498c2ecf20Sopenharmony_ci *offset = noffset; 19508c2ecf20Sopenharmony_ci } 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci he_cap = ieee80211_get_he_sta_cap(sband); 19538c2ecf20Sopenharmony_ci if (he_cap) { 19548c2ecf20Sopenharmony_ci pos = ieee80211_ie_build_he_cap(pos, he_cap, end); 19558c2ecf20Sopenharmony_ci if (!pos) 19568c2ecf20Sopenharmony_ci goto out_err; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (sband->band == NL80211_BAND_6GHZ) { 19598c2ecf20Sopenharmony_ci enum nl80211_iftype iftype = 19608c2ecf20Sopenharmony_ci ieee80211_vif_type_p2p(&sdata->vif); 19618c2ecf20Sopenharmony_ci __le16 cap = ieee80211_get_he_6ghz_capa(sband, iftype); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci pos = ieee80211_write_he_6ghz_cap(pos, cap, end); 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci /* 19688c2ecf20Sopenharmony_ci * If adding more here, adjust code in main.c 19698c2ecf20Sopenharmony_ci * that calculates local->scan_ies_len. 19708c2ecf20Sopenharmony_ci */ 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci return pos - buffer; 19738c2ecf20Sopenharmony_ci out_err: 19748c2ecf20Sopenharmony_ci WARN_ONCE(1, "not enough space for preq IEs\n"); 19758c2ecf20Sopenharmony_ci done: 19768c2ecf20Sopenharmony_ci return pos - buffer; 19778c2ecf20Sopenharmony_ci} 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ciint ieee80211_build_preq_ies(struct ieee80211_sub_if_data *sdata, u8 *buffer, 19808c2ecf20Sopenharmony_ci size_t buffer_len, 19818c2ecf20Sopenharmony_ci struct ieee80211_scan_ies *ie_desc, 19828c2ecf20Sopenharmony_ci const u8 *ie, size_t ie_len, 19838c2ecf20Sopenharmony_ci u8 bands_used, u32 *rate_masks, 19848c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef, 19858c2ecf20Sopenharmony_ci u32 flags) 19868c2ecf20Sopenharmony_ci{ 19878c2ecf20Sopenharmony_ci size_t pos = 0, old_pos = 0, custom_ie_offset = 0; 19888c2ecf20Sopenharmony_ci int i; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci memset(ie_desc, 0, sizeof(*ie_desc)); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci for (i = 0; i < NUM_NL80211_BANDS; i++) { 19938c2ecf20Sopenharmony_ci if (bands_used & BIT(i)) { 19948c2ecf20Sopenharmony_ci pos += ieee80211_build_preq_ies_band(sdata, 19958c2ecf20Sopenharmony_ci buffer + pos, 19968c2ecf20Sopenharmony_ci buffer_len - pos, 19978c2ecf20Sopenharmony_ci ie, ie_len, i, 19988c2ecf20Sopenharmony_ci rate_masks[i], 19998c2ecf20Sopenharmony_ci chandef, 20008c2ecf20Sopenharmony_ci &custom_ie_offset, 20018c2ecf20Sopenharmony_ci flags); 20028c2ecf20Sopenharmony_ci ie_desc->ies[i] = buffer + old_pos; 20038c2ecf20Sopenharmony_ci ie_desc->len[i] = pos - old_pos; 20048c2ecf20Sopenharmony_ci old_pos = pos; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* add any remaining custom IEs */ 20098c2ecf20Sopenharmony_ci if (ie && ie_len) { 20108c2ecf20Sopenharmony_ci if (WARN_ONCE(buffer_len - pos < ie_len - custom_ie_offset, 20118c2ecf20Sopenharmony_ci "not enough space for preq custom IEs\n")) 20128c2ecf20Sopenharmony_ci return pos; 20138c2ecf20Sopenharmony_ci memcpy(buffer + pos, ie + custom_ie_offset, 20148c2ecf20Sopenharmony_ci ie_len - custom_ie_offset); 20158c2ecf20Sopenharmony_ci ie_desc->common_ies = buffer + pos; 20168c2ecf20Sopenharmony_ci ie_desc->common_ie_len = ie_len - custom_ie_offset; 20178c2ecf20Sopenharmony_ci pos += ie_len - custom_ie_offset; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci return pos; 20218c2ecf20Sopenharmony_ci}; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistruct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, 20248c2ecf20Sopenharmony_ci const u8 *src, const u8 *dst, 20258c2ecf20Sopenharmony_ci u32 ratemask, 20268c2ecf20Sopenharmony_ci struct ieee80211_channel *chan, 20278c2ecf20Sopenharmony_ci const u8 *ssid, size_t ssid_len, 20288c2ecf20Sopenharmony_ci const u8 *ie, size_t ie_len, 20298c2ecf20Sopenharmony_ci u32 flags) 20308c2ecf20Sopenharmony_ci{ 20318c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 20328c2ecf20Sopenharmony_ci struct cfg80211_chan_def chandef; 20338c2ecf20Sopenharmony_ci struct sk_buff *skb; 20348c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 20358c2ecf20Sopenharmony_ci int ies_len; 20368c2ecf20Sopenharmony_ci u32 rate_masks[NUM_NL80211_BANDS] = {}; 20378c2ecf20Sopenharmony_ci struct ieee80211_scan_ies dummy_ie_desc; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci /* 20408c2ecf20Sopenharmony_ci * Do not send DS Channel parameter for directed probe requests 20418c2ecf20Sopenharmony_ci * in order to maximize the chance that we get a response. Some 20428c2ecf20Sopenharmony_ci * badly-behaved APs don't respond when this parameter is included. 20438c2ecf20Sopenharmony_ci */ 20448c2ecf20Sopenharmony_ci chandef.width = sdata->vif.bss_conf.chandef.width; 20458c2ecf20Sopenharmony_ci if (flags & IEEE80211_PROBE_FLAG_DIRECTED) 20468c2ecf20Sopenharmony_ci chandef.chan = NULL; 20478c2ecf20Sopenharmony_ci else 20488c2ecf20Sopenharmony_ci chandef.chan = chan; 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len, 20518c2ecf20Sopenharmony_ci 100 + ie_len); 20528c2ecf20Sopenharmony_ci if (!skb) 20538c2ecf20Sopenharmony_ci return NULL; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci rate_masks[chan->band] = ratemask; 20568c2ecf20Sopenharmony_ci ies_len = ieee80211_build_preq_ies(sdata, skb_tail_pointer(skb), 20578c2ecf20Sopenharmony_ci skb_tailroom(skb), &dummy_ie_desc, 20588c2ecf20Sopenharmony_ci ie, ie_len, BIT(chan->band), 20598c2ecf20Sopenharmony_ci rate_masks, &chandef, flags); 20608c2ecf20Sopenharmony_ci skb_put(skb, ies_len); 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci if (dst) { 20638c2ecf20Sopenharmony_ci mgmt = (struct ieee80211_mgmt *) skb->data; 20648c2ecf20Sopenharmony_ci memcpy(mgmt->da, dst, ETH_ALEN); 20658c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, dst, ETH_ALEN); 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci return skb; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ciu32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, 20748c2ecf20Sopenharmony_ci struct ieee802_11_elems *elems, 20758c2ecf20Sopenharmony_ci enum nl80211_band band, u32 *basic_rates) 20768c2ecf20Sopenharmony_ci{ 20778c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 20788c2ecf20Sopenharmony_ci size_t num_rates; 20798c2ecf20Sopenharmony_ci u32 supp_rates, rate_flags; 20808c2ecf20Sopenharmony_ci int i, j, shift; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci sband = sdata->local->hw.wiphy->bands[band]; 20838c2ecf20Sopenharmony_ci if (WARN_ON(!sband)) 20848c2ecf20Sopenharmony_ci return 1; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 20878c2ecf20Sopenharmony_ci shift = ieee80211_vif_get_shift(&sdata->vif); 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci num_rates = sband->n_bitrates; 20908c2ecf20Sopenharmony_ci supp_rates = 0; 20918c2ecf20Sopenharmony_ci for (i = 0; i < elems->supp_rates_len + 20928c2ecf20Sopenharmony_ci elems->ext_supp_rates_len; i++) { 20938c2ecf20Sopenharmony_ci u8 rate = 0; 20948c2ecf20Sopenharmony_ci int own_rate; 20958c2ecf20Sopenharmony_ci bool is_basic; 20968c2ecf20Sopenharmony_ci if (i < elems->supp_rates_len) 20978c2ecf20Sopenharmony_ci rate = elems->supp_rates[i]; 20988c2ecf20Sopenharmony_ci else if (elems->ext_supp_rates) 20998c2ecf20Sopenharmony_ci rate = elems->ext_supp_rates 21008c2ecf20Sopenharmony_ci [i - elems->supp_rates_len]; 21018c2ecf20Sopenharmony_ci own_rate = 5 * (rate & 0x7f); 21028c2ecf20Sopenharmony_ci is_basic = !!(rate & 0x80); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (is_basic && (rate & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) 21058c2ecf20Sopenharmony_ci continue; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci for (j = 0; j < num_rates; j++) { 21088c2ecf20Sopenharmony_ci int brate; 21098c2ecf20Sopenharmony_ci if ((rate_flags & sband->bitrates[j].flags) 21108c2ecf20Sopenharmony_ci != rate_flags) 21118c2ecf20Sopenharmony_ci continue; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci brate = DIV_ROUND_UP(sband->bitrates[j].bitrate, 21148c2ecf20Sopenharmony_ci 1 << shift); 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci if (brate == own_rate) { 21178c2ecf20Sopenharmony_ci supp_rates |= BIT(j); 21188c2ecf20Sopenharmony_ci if (basic_rates && is_basic) 21198c2ecf20Sopenharmony_ci *basic_rates |= BIT(j); 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci return supp_rates; 21248c2ecf20Sopenharmony_ci} 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_civoid ieee80211_stop_device(struct ieee80211_local *local) 21278c2ecf20Sopenharmony_ci{ 21288c2ecf20Sopenharmony_ci ieee80211_led_radio(local, false); 21298c2ecf20Sopenharmony_ci ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO); 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci cancel_work_sync(&local->reconfig_filter); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci flush_workqueue(local->workqueue); 21348c2ecf20Sopenharmony_ci drv_stop(local); 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_cistatic void ieee80211_flush_completed_scan(struct ieee80211_local *local, 21388c2ecf20Sopenharmony_ci bool aborted) 21398c2ecf20Sopenharmony_ci{ 21408c2ecf20Sopenharmony_ci /* It's possible that we don't handle the scan completion in 21418c2ecf20Sopenharmony_ci * time during suspend, so if it's still marked as completed 21428c2ecf20Sopenharmony_ci * here, queue the work and flush it to clean things up. 21438c2ecf20Sopenharmony_ci * Instead of calling the worker function directly here, we 21448c2ecf20Sopenharmony_ci * really queue it to avoid potential races with other flows 21458c2ecf20Sopenharmony_ci * scheduling the same work. 21468c2ecf20Sopenharmony_ci */ 21478c2ecf20Sopenharmony_ci if (test_bit(SCAN_COMPLETED, &local->scanning)) { 21488c2ecf20Sopenharmony_ci /* If coming from reconfiguration failure, abort the scan so 21498c2ecf20Sopenharmony_ci * we don't attempt to continue a partial HW scan - which is 21508c2ecf20Sopenharmony_ci * possible otherwise if (e.g.) the 2.4 GHz portion was the 21518c2ecf20Sopenharmony_ci * completed scan, and a 5 GHz portion is still pending. 21528c2ecf20Sopenharmony_ci */ 21538c2ecf20Sopenharmony_ci if (aborted) 21548c2ecf20Sopenharmony_ci set_bit(SCAN_ABORTED, &local->scanning); 21558c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); 21568c2ecf20Sopenharmony_ci flush_delayed_work(&local->scan_work); 21578c2ecf20Sopenharmony_ci } 21588c2ecf20Sopenharmony_ci} 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_cistatic void ieee80211_handle_reconfig_failure(struct ieee80211_local *local) 21618c2ecf20Sopenharmony_ci{ 21628c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 21638c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci /* 21668c2ecf20Sopenharmony_ci * We get here if during resume the device can't be restarted properly. 21678c2ecf20Sopenharmony_ci * We might also get here if this happens during HW reset, which is a 21688c2ecf20Sopenharmony_ci * slightly different situation and we need to drop all connections in 21698c2ecf20Sopenharmony_ci * the latter case. 21708c2ecf20Sopenharmony_ci * 21718c2ecf20Sopenharmony_ci * Ask cfg80211 to turn off all interfaces, this will result in more 21728c2ecf20Sopenharmony_ci * warnings but at least we'll then get into a clean stopped state. 21738c2ecf20Sopenharmony_ci */ 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci local->resuming = false; 21768c2ecf20Sopenharmony_ci local->suspended = false; 21778c2ecf20Sopenharmony_ci local->in_reconfig = false; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci ieee80211_flush_completed_scan(local, true); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* scheduled scan clearly can't be running any more, but tell 21828c2ecf20Sopenharmony_ci * cfg80211 and clear local state 21838c2ecf20Sopenharmony_ci */ 21848c2ecf20Sopenharmony_ci ieee80211_sched_scan_end(local); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) 21878c2ecf20Sopenharmony_ci sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci /* Mark channel contexts as not being in the driver any more to avoid 21908c2ecf20Sopenharmony_ci * removing them from the driver during the shutdown process... 21918c2ecf20Sopenharmony_ci */ 21928c2ecf20Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 21938c2ecf20Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) 21948c2ecf20Sopenharmony_ci ctx->driver_present = false; 21958c2ecf20Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci cfg80211_shutdown_all_interfaces(local->hw.wiphy); 21988c2ecf20Sopenharmony_ci} 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_cistatic void ieee80211_assign_chanctx(struct ieee80211_local *local, 22018c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 22028c2ecf20Sopenharmony_ci{ 22038c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *conf; 22048c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci if (!local->use_chanctx) 22078c2ecf20Sopenharmony_ci return; 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 22108c2ecf20Sopenharmony_ci conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 22118c2ecf20Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 22128c2ecf20Sopenharmony_ci if (conf) { 22138c2ecf20Sopenharmony_ci ctx = container_of(conf, struct ieee80211_chanctx, conf); 22148c2ecf20Sopenharmony_ci drv_assign_vif_chanctx(local, sdata, ctx); 22158c2ecf20Sopenharmony_ci } 22168c2ecf20Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 22178c2ecf20Sopenharmony_ci} 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_cistatic void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata) 22208c2ecf20Sopenharmony_ci{ 22218c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 22228c2ecf20Sopenharmony_ci struct sta_info *sta; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci /* add STAs back */ 22258c2ecf20Sopenharmony_ci mutex_lock(&local->sta_mtx); 22268c2ecf20Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 22278c2ecf20Sopenharmony_ci enum ieee80211_sta_state state; 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci if (!sta->uploaded || sta->sdata != sdata) 22308c2ecf20Sopenharmony_ci continue; 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci for (state = IEEE80211_STA_NOTEXIST; 22338c2ecf20Sopenharmony_ci state < sta->sta_state; state++) 22348c2ecf20Sopenharmony_ci WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 22358c2ecf20Sopenharmony_ci state + 1)); 22368c2ecf20Sopenharmony_ci } 22378c2ecf20Sopenharmony_ci mutex_unlock(&local->sta_mtx); 22388c2ecf20Sopenharmony_ci} 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_cistatic int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci struct cfg80211_nan_func *func, **funcs; 22438c2ecf20Sopenharmony_ci int res, id, i = 0; 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci res = drv_start_nan(sdata->local, sdata, 22468c2ecf20Sopenharmony_ci &sdata->u.nan.conf); 22478c2ecf20Sopenharmony_ci if (WARN_ON(res)) 22488c2ecf20Sopenharmony_ci return res; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci funcs = kcalloc(sdata->local->hw.max_nan_de_entries + 1, 22518c2ecf20Sopenharmony_ci sizeof(*funcs), 22528c2ecf20Sopenharmony_ci GFP_KERNEL); 22538c2ecf20Sopenharmony_ci if (!funcs) 22548c2ecf20Sopenharmony_ci return -ENOMEM; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci /* Add all the functions: 22578c2ecf20Sopenharmony_ci * This is a little bit ugly. We need to call a potentially sleeping 22588c2ecf20Sopenharmony_ci * callback for each NAN function, so we can't hold the spinlock. 22598c2ecf20Sopenharmony_ci */ 22608c2ecf20Sopenharmony_ci spin_lock_bh(&sdata->u.nan.func_lock); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) 22638c2ecf20Sopenharmony_ci funcs[i++] = func; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci spin_unlock_bh(&sdata->u.nan.func_lock); 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci for (i = 0; funcs[i]; i++) { 22688c2ecf20Sopenharmony_ci res = drv_add_nan_func(sdata->local, sdata, funcs[i]); 22698c2ecf20Sopenharmony_ci if (WARN_ON(res)) 22708c2ecf20Sopenharmony_ci ieee80211_nan_func_terminated(&sdata->vif, 22718c2ecf20Sopenharmony_ci funcs[i]->instance_id, 22728c2ecf20Sopenharmony_ci NL80211_NAN_FUNC_TERM_REASON_ERROR, 22738c2ecf20Sopenharmony_ci GFP_KERNEL); 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci kfree(funcs); 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci return 0; 22798c2ecf20Sopenharmony_ci} 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ciint ieee80211_reconfig(struct ieee80211_local *local) 22828c2ecf20Sopenharmony_ci{ 22838c2ecf20Sopenharmony_ci struct ieee80211_hw *hw = &local->hw; 22848c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 22858c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx; 22868c2ecf20Sopenharmony_ci struct sta_info *sta; 22878c2ecf20Sopenharmony_ci int res, i; 22888c2ecf20Sopenharmony_ci bool reconfig_due_to_wowlan = false; 22898c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sched_scan_sdata; 22908c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *sched_scan_req; 22918c2ecf20Sopenharmony_ci bool sched_scan_stopped = false; 22928c2ecf20Sopenharmony_ci bool suspended = local->suspended; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci /* nothing to do if HW shouldn't run */ 22958c2ecf20Sopenharmony_ci if (!local->open_count) 22968c2ecf20Sopenharmony_ci goto wake_up; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 22998c2ecf20Sopenharmony_ci if (suspended) 23008c2ecf20Sopenharmony_ci local->resuming = true; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci if (local->wowlan) { 23038c2ecf20Sopenharmony_ci /* 23048c2ecf20Sopenharmony_ci * In the wowlan case, both mac80211 and the device 23058c2ecf20Sopenharmony_ci * are functional when the resume op is called, so 23068c2ecf20Sopenharmony_ci * clear local->suspended so the device could operate 23078c2ecf20Sopenharmony_ci * normally (e.g. pass rx frames). 23088c2ecf20Sopenharmony_ci */ 23098c2ecf20Sopenharmony_ci local->suspended = false; 23108c2ecf20Sopenharmony_ci res = drv_resume(local); 23118c2ecf20Sopenharmony_ci local->wowlan = false; 23128c2ecf20Sopenharmony_ci if (res < 0) { 23138c2ecf20Sopenharmony_ci local->resuming = false; 23148c2ecf20Sopenharmony_ci return res; 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci if (res == 0) 23178c2ecf20Sopenharmony_ci goto wake_up; 23188c2ecf20Sopenharmony_ci WARN_ON(res > 1); 23198c2ecf20Sopenharmony_ci /* 23208c2ecf20Sopenharmony_ci * res is 1, which means the driver requested 23218c2ecf20Sopenharmony_ci * to go through a regular reset on wakeup. 23228c2ecf20Sopenharmony_ci * restore local->suspended in this case. 23238c2ecf20Sopenharmony_ci */ 23248c2ecf20Sopenharmony_ci reconfig_due_to_wowlan = true; 23258c2ecf20Sopenharmony_ci local->suspended = true; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci#endif 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci /* 23308c2ecf20Sopenharmony_ci * In case of hw_restart during suspend (without wowlan), 23318c2ecf20Sopenharmony_ci * cancel restart work, as we are reconfiguring the device 23328c2ecf20Sopenharmony_ci * anyway. 23338c2ecf20Sopenharmony_ci * Note that restart_work is scheduled on a frozen workqueue, 23348c2ecf20Sopenharmony_ci * so we can't deadlock in this case. 23358c2ecf20Sopenharmony_ci */ 23368c2ecf20Sopenharmony_ci if (suspended && local->in_reconfig && !reconfig_due_to_wowlan) 23378c2ecf20Sopenharmony_ci cancel_work_sync(&local->restart_work); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci local->started = false; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci /* 23428c2ecf20Sopenharmony_ci * Upon resume hardware can sometimes be goofy due to 23438c2ecf20Sopenharmony_ci * various platform / driver / bus issues, so restarting 23448c2ecf20Sopenharmony_ci * the device may at times not work immediately. Propagate 23458c2ecf20Sopenharmony_ci * the error. 23468c2ecf20Sopenharmony_ci */ 23478c2ecf20Sopenharmony_ci res = drv_start(local); 23488c2ecf20Sopenharmony_ci if (res) { 23498c2ecf20Sopenharmony_ci if (suspended) 23508c2ecf20Sopenharmony_ci WARN(1, "Hardware became unavailable upon resume. This could be a software issue prior to suspend or a hardware issue.\n"); 23518c2ecf20Sopenharmony_ci else 23528c2ecf20Sopenharmony_ci WARN(1, "Hardware became unavailable during restart.\n"); 23538c2ecf20Sopenharmony_ci ieee80211_handle_reconfig_failure(local); 23548c2ecf20Sopenharmony_ci return res; 23558c2ecf20Sopenharmony_ci } 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci /* setup fragmentation threshold */ 23588c2ecf20Sopenharmony_ci drv_set_frag_threshold(local, hw->wiphy->frag_threshold); 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci /* setup RTS threshold */ 23618c2ecf20Sopenharmony_ci drv_set_rts_threshold(local, hw->wiphy->rts_threshold); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci /* reset coverage class */ 23648c2ecf20Sopenharmony_ci drv_set_coverage_class(local, hw->wiphy->coverage_class); 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci ieee80211_led_radio(local, true); 23678c2ecf20Sopenharmony_ci ieee80211_mod_tpt_led_trig(local, 23688c2ecf20Sopenharmony_ci IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci /* add interfaces */ 23718c2ecf20Sopenharmony_ci sdata = rtnl_dereference(local->monitor_sdata); 23728c2ecf20Sopenharmony_ci if (sdata) { 23738c2ecf20Sopenharmony_ci /* in HW restart it exists already */ 23748c2ecf20Sopenharmony_ci WARN_ON(local->resuming); 23758c2ecf20Sopenharmony_ci res = drv_add_interface(local, sdata); 23768c2ecf20Sopenharmony_ci if (WARN_ON(res)) { 23778c2ecf20Sopenharmony_ci RCU_INIT_POINTER(local->monitor_sdata, NULL); 23788c2ecf20Sopenharmony_ci synchronize_net(); 23798c2ecf20Sopenharmony_ci kfree(sdata); 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci } 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 23848c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 23858c2ecf20Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_MONITOR && 23868c2ecf20Sopenharmony_ci ieee80211_sdata_running(sdata)) { 23878c2ecf20Sopenharmony_ci res = drv_add_interface(local, sdata); 23888c2ecf20Sopenharmony_ci if (WARN_ON(res)) 23898c2ecf20Sopenharmony_ci break; 23908c2ecf20Sopenharmony_ci } 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci /* If adding any of the interfaces failed above, roll back and 23948c2ecf20Sopenharmony_ci * report failure. 23958c2ecf20Sopenharmony_ci */ 23968c2ecf20Sopenharmony_ci if (res) { 23978c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(sdata, &local->interfaces, 23988c2ecf20Sopenharmony_ci list) 23998c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && 24008c2ecf20Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_MONITOR && 24018c2ecf20Sopenharmony_ci ieee80211_sdata_running(sdata)) 24028c2ecf20Sopenharmony_ci drv_remove_interface(local, sdata); 24038c2ecf20Sopenharmony_ci ieee80211_handle_reconfig_failure(local); 24048c2ecf20Sopenharmony_ci return res; 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* add channel contexts */ 24088c2ecf20Sopenharmony_ci if (local->use_chanctx) { 24098c2ecf20Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 24108c2ecf20Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) 24118c2ecf20Sopenharmony_ci if (ctx->replace_state != 24128c2ecf20Sopenharmony_ci IEEE80211_CHANCTX_REPLACES_OTHER) 24138c2ecf20Sopenharmony_ci WARN_ON(drv_add_chanctx(local, ctx)); 24148c2ecf20Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci sdata = rtnl_dereference(local->monitor_sdata); 24178c2ecf20Sopenharmony_ci if (sdata && ieee80211_sdata_running(sdata)) 24188c2ecf20Sopenharmony_ci ieee80211_assign_chanctx(local, sdata); 24198c2ecf20Sopenharmony_ci } 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci /* reconfigure hardware */ 24228c2ecf20Sopenharmony_ci ieee80211_hw_config(local, ~0); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci ieee80211_configure_filter(local); 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci /* Finally also reconfigure all the BSS information */ 24278c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 24288c2ecf20Sopenharmony_ci u32 changed; 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 24318c2ecf20Sopenharmony_ci continue; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci ieee80211_assign_chanctx(local, sdata); 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci switch (sdata->vif.type) { 24368c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 24378c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 24388c2ecf20Sopenharmony_ci break; 24398c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 24408c2ecf20Sopenharmony_ci if (sdata->vif.bss_conf.ibss_joined) 24418c2ecf20Sopenharmony_ci WARN_ON(drv_join_ibss(local, sdata)); 24428c2ecf20Sopenharmony_ci fallthrough; 24438c2ecf20Sopenharmony_ci default: 24448c2ecf20Sopenharmony_ci ieee80211_reconfig_stations(sdata); 24458c2ecf20Sopenharmony_ci fallthrough; 24468c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: /* AP stations are handled later */ 24478c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_NUM_ACS; i++) 24488c2ecf20Sopenharmony_ci drv_conf_tx(local, sdata, i, 24498c2ecf20Sopenharmony_ci &sdata->tx_conf[i]); 24508c2ecf20Sopenharmony_ci break; 24518c2ecf20Sopenharmony_ci } 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci /* common change flags for all interface types */ 24548c2ecf20Sopenharmony_ci changed = BSS_CHANGED_ERP_CTS_PROT | 24558c2ecf20Sopenharmony_ci BSS_CHANGED_ERP_PREAMBLE | 24568c2ecf20Sopenharmony_ci BSS_CHANGED_ERP_SLOT | 24578c2ecf20Sopenharmony_ci BSS_CHANGED_HT | 24588c2ecf20Sopenharmony_ci BSS_CHANGED_BASIC_RATES | 24598c2ecf20Sopenharmony_ci BSS_CHANGED_BEACON_INT | 24608c2ecf20Sopenharmony_ci BSS_CHANGED_BSSID | 24618c2ecf20Sopenharmony_ci BSS_CHANGED_CQM | 24628c2ecf20Sopenharmony_ci BSS_CHANGED_QOS | 24638c2ecf20Sopenharmony_ci BSS_CHANGED_IDLE | 24648c2ecf20Sopenharmony_ci BSS_CHANGED_TXPOWER | 24658c2ecf20Sopenharmony_ci BSS_CHANGED_MCAST_RATE; 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci if (sdata->vif.mu_mimo_owner) 24688c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_MU_GROUPS; 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci switch (sdata->vif.type) { 24718c2ecf20Sopenharmony_ci case NL80211_IFTYPE_STATION: 24728c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_ASSOC | 24738c2ecf20Sopenharmony_ci BSS_CHANGED_ARP_FILTER | 24748c2ecf20Sopenharmony_ci BSS_CHANGED_PS; 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci /* Re-send beacon info report to the driver */ 24778c2ecf20Sopenharmony_ci if (sdata->u.mgd.have_beacon) 24788c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_BEACON_INFO; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci if (sdata->vif.bss_conf.max_idle_period || 24818c2ecf20Sopenharmony_ci sdata->vif.bss_conf.protected_keep_alive) 24828c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_KEEP_ALIVE; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci sdata_lock(sdata); 24858c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 24868c2ecf20Sopenharmony_ci sdata_unlock(sdata); 24878c2ecf20Sopenharmony_ci break; 24888c2ecf20Sopenharmony_ci case NL80211_IFTYPE_OCB: 24898c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_OCB; 24908c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 24918c2ecf20Sopenharmony_ci break; 24928c2ecf20Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 24938c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_IBSS; 24948c2ecf20Sopenharmony_ci fallthrough; 24958c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP: 24968c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci if (sdata->vif.bss_conf.ftm_responder == 1 && 24998c2ecf20Sopenharmony_ci wiphy_ext_feature_isset(sdata->local->hw.wiphy, 25008c2ecf20Sopenharmony_ci NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) 25018c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_FTM_RESPONDER; 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP) { 25048c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_AP_PROBE_RESP; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci if (rcu_access_pointer(sdata->u.ap.beacon)) 25078c2ecf20Sopenharmony_ci drv_start_ap(local, sdata); 25088c2ecf20Sopenharmony_ci } 25098c2ecf20Sopenharmony_ci fallthrough; 25108c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 25118c2ecf20Sopenharmony_ci if (sdata->vif.bss_conf.enable_beacon) { 25128c2ecf20Sopenharmony_ci changed |= BSS_CHANGED_BEACON | 25138c2ecf20Sopenharmony_ci BSS_CHANGED_BEACON_ENABLED; 25148c2ecf20Sopenharmony_ci ieee80211_bss_info_change_notify(sdata, changed); 25158c2ecf20Sopenharmony_ci } 25168c2ecf20Sopenharmony_ci break; 25178c2ecf20Sopenharmony_ci case NL80211_IFTYPE_NAN: 25188c2ecf20Sopenharmony_ci res = ieee80211_reconfig_nan(sdata); 25198c2ecf20Sopenharmony_ci if (res < 0) { 25208c2ecf20Sopenharmony_ci ieee80211_handle_reconfig_failure(local); 25218c2ecf20Sopenharmony_ci return res; 25228c2ecf20Sopenharmony_ci } 25238c2ecf20Sopenharmony_ci break; 25248c2ecf20Sopenharmony_ci case NL80211_IFTYPE_WDS: 25258c2ecf20Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 25268c2ecf20Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 25278c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 25288c2ecf20Sopenharmony_ci /* nothing to do */ 25298c2ecf20Sopenharmony_ci break; 25308c2ecf20Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 25318c2ecf20Sopenharmony_ci case NUM_NL80211_IFTYPES: 25328c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 25338c2ecf20Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 25348c2ecf20Sopenharmony_ci WARN_ON(1); 25358c2ecf20Sopenharmony_ci break; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci } 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci ieee80211_recalc_ps(local); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci /* 25428c2ecf20Sopenharmony_ci * The sta might be in psm against the ap (e.g. because 25438c2ecf20Sopenharmony_ci * this was the state before a hw restart), so we 25448c2ecf20Sopenharmony_ci * explicitly send a null packet in order to make sure 25458c2ecf20Sopenharmony_ci * it'll sync against the ap (and get out of psm). 25468c2ecf20Sopenharmony_ci */ 25478c2ecf20Sopenharmony_ci if (!(local->hw.conf.flags & IEEE80211_CONF_PS)) { 25488c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 25498c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_STATION) 25508c2ecf20Sopenharmony_ci continue; 25518c2ecf20Sopenharmony_ci if (!sdata->u.mgd.associated) 25528c2ecf20Sopenharmony_ci continue; 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci ieee80211_send_nullfunc(local, sdata, false); 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci } 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci /* APs are now beaconing, add back stations */ 25598c2ecf20Sopenharmony_ci mutex_lock(&local->sta_mtx); 25608c2ecf20Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 25618c2ecf20Sopenharmony_ci enum ieee80211_sta_state state; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci if (!sta->uploaded) 25648c2ecf20Sopenharmony_ci continue; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci if (sta->sdata->vif.type != NL80211_IFTYPE_AP && 25678c2ecf20Sopenharmony_ci sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN) 25688c2ecf20Sopenharmony_ci continue; 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci for (state = IEEE80211_STA_NOTEXIST; 25718c2ecf20Sopenharmony_ci state < sta->sta_state; state++) 25728c2ecf20Sopenharmony_ci WARN_ON(drv_sta_state(local, sta->sdata, sta, state, 25738c2ecf20Sopenharmony_ci state + 1)); 25748c2ecf20Sopenharmony_ci } 25758c2ecf20Sopenharmony_ci mutex_unlock(&local->sta_mtx); 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci /* add back keys */ 25788c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) 25798c2ecf20Sopenharmony_ci ieee80211_reenable_keys(sdata); 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci /* Reconfigure sched scan if it was interrupted by FW restart */ 25828c2ecf20Sopenharmony_ci mutex_lock(&local->mtx); 25838c2ecf20Sopenharmony_ci sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, 25848c2ecf20Sopenharmony_ci lockdep_is_held(&local->mtx)); 25858c2ecf20Sopenharmony_ci sched_scan_req = rcu_dereference_protected(local->sched_scan_req, 25868c2ecf20Sopenharmony_ci lockdep_is_held(&local->mtx)); 25878c2ecf20Sopenharmony_ci if (sched_scan_sdata && sched_scan_req) 25888c2ecf20Sopenharmony_ci /* 25898c2ecf20Sopenharmony_ci * Sched scan stopped, but we don't want to report it. Instead, 25908c2ecf20Sopenharmony_ci * we're trying to reschedule. However, if more than one scan 25918c2ecf20Sopenharmony_ci * plan was set, we cannot reschedule since we don't know which 25928c2ecf20Sopenharmony_ci * scan plan was currently running (and some scan plans may have 25938c2ecf20Sopenharmony_ci * already finished). 25948c2ecf20Sopenharmony_ci */ 25958c2ecf20Sopenharmony_ci if (sched_scan_req->n_scan_plans > 1 || 25968c2ecf20Sopenharmony_ci __ieee80211_request_sched_scan_start(sched_scan_sdata, 25978c2ecf20Sopenharmony_ci sched_scan_req)) { 25988c2ecf20Sopenharmony_ci RCU_INIT_POINTER(local->sched_scan_sdata, NULL); 25998c2ecf20Sopenharmony_ci RCU_INIT_POINTER(local->sched_scan_req, NULL); 26008c2ecf20Sopenharmony_ci sched_scan_stopped = true; 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci mutex_unlock(&local->mtx); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci if (sched_scan_stopped) 26058c2ecf20Sopenharmony_ci cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy, 0); 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci wake_up: 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci if (local->monitors == local->open_count && local->monitors > 0) 26108c2ecf20Sopenharmony_ci ieee80211_add_virtual_monitor(local); 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci /* 26138c2ecf20Sopenharmony_ci * Clear the WLAN_STA_BLOCK_BA flag so new aggregation 26148c2ecf20Sopenharmony_ci * sessions can be established after a resume. 26158c2ecf20Sopenharmony_ci * 26168c2ecf20Sopenharmony_ci * Also tear down aggregation sessions since reconfiguring 26178c2ecf20Sopenharmony_ci * them in a hardware restart scenario is not easily done 26188c2ecf20Sopenharmony_ci * right now, and the hardware will have lost information 26198c2ecf20Sopenharmony_ci * about the sessions, but we and the AP still think they 26208c2ecf20Sopenharmony_ci * are active. This is really a workaround though. 26218c2ecf20Sopenharmony_ci */ 26228c2ecf20Sopenharmony_ci if (ieee80211_hw_check(hw, AMPDU_AGGREGATION)) { 26238c2ecf20Sopenharmony_ci mutex_lock(&local->sta_mtx); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 26268c2ecf20Sopenharmony_ci if (!local->resuming) 26278c2ecf20Sopenharmony_ci ieee80211_sta_tear_down_BA_sessions( 26288c2ecf20Sopenharmony_ci sta, AGG_STOP_LOCAL_REQUEST); 26298c2ecf20Sopenharmony_ci clear_sta_flag(sta, WLAN_STA_BLOCK_BA); 26308c2ecf20Sopenharmony_ci } 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci mutex_unlock(&local->sta_mtx); 26338c2ecf20Sopenharmony_ci } 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci if (local->in_reconfig) { 26368c2ecf20Sopenharmony_ci local->in_reconfig = false; 26378c2ecf20Sopenharmony_ci barrier(); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci /* Restart deferred ROCs */ 26408c2ecf20Sopenharmony_ci mutex_lock(&local->mtx); 26418c2ecf20Sopenharmony_ci ieee80211_start_next_roc(local); 26428c2ecf20Sopenharmony_ci mutex_unlock(&local->mtx); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci /* Requeue all works */ 26458c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) 26468c2ecf20Sopenharmony_ci ieee80211_queue_work(&local->hw, &sdata->work); 26478c2ecf20Sopenharmony_ci } 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci ieee80211_wake_queues_by_reason(hw, IEEE80211_MAX_QUEUE_MAP, 26508c2ecf20Sopenharmony_ci IEEE80211_QUEUE_STOP_REASON_SUSPEND, 26518c2ecf20Sopenharmony_ci false); 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci /* 26548c2ecf20Sopenharmony_ci * If this is for hw restart things are still running. 26558c2ecf20Sopenharmony_ci * We may want to change that later, however. 26568c2ecf20Sopenharmony_ci */ 26578c2ecf20Sopenharmony_ci if (local->open_count && (!suspended || reconfig_due_to_wowlan)) 26588c2ecf20Sopenharmony_ci drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci if (!suspended) 26618c2ecf20Sopenharmony_ci return 0; 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 26648c2ecf20Sopenharmony_ci /* first set suspended false, then resuming */ 26658c2ecf20Sopenharmony_ci local->suspended = false; 26668c2ecf20Sopenharmony_ci mb(); 26678c2ecf20Sopenharmony_ci local->resuming = false; 26688c2ecf20Sopenharmony_ci 26698c2ecf20Sopenharmony_ci ieee80211_flush_completed_scan(local, false); 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci if (local->open_count && !reconfig_due_to_wowlan) 26728c2ecf20Sopenharmony_ci drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 26758c2ecf20Sopenharmony_ci if (!ieee80211_sdata_running(sdata)) 26768c2ecf20Sopenharmony_ci continue; 26778c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) 26788c2ecf20Sopenharmony_ci ieee80211_sta_restart(sdata); 26798c2ecf20Sopenharmony_ci } 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci mod_timer(&local->sta_cleanup, jiffies + 1); 26828c2ecf20Sopenharmony_ci#else 26838c2ecf20Sopenharmony_ci WARN_ON(1); 26848c2ecf20Sopenharmony_ci#endif 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_ci return 0; 26878c2ecf20Sopenharmony_ci} 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_civoid ieee80211_resume_disconnect(struct ieee80211_vif *vif) 26908c2ecf20Sopenharmony_ci{ 26918c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 26928c2ecf20Sopenharmony_ci struct ieee80211_local *local; 26938c2ecf20Sopenharmony_ci struct ieee80211_key *key; 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci if (WARN_ON(!vif)) 26968c2ecf20Sopenharmony_ci return; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci sdata = vif_to_sdata(vif); 26998c2ecf20Sopenharmony_ci local = sdata->local; 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci if (WARN_ON(!local->resuming)) 27028c2ecf20Sopenharmony_ci return; 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) 27058c2ecf20Sopenharmony_ci return; 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci sdata->flags |= IEEE80211_SDATA_DISCONNECT_RESUME; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci mutex_lock(&local->key_mtx); 27108c2ecf20Sopenharmony_ci list_for_each_entry(key, &sdata->key_list, list) 27118c2ecf20Sopenharmony_ci key->flags |= KEY_FLAG_TAINTED; 27128c2ecf20Sopenharmony_ci mutex_unlock(&local->key_mtx); 27138c2ecf20Sopenharmony_ci} 27148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_resume_disconnect); 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_civoid ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata) 27178c2ecf20Sopenharmony_ci{ 27188c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 27198c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 27208c2ecf20Sopenharmony_ci struct ieee80211_chanctx *chanctx; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 27258c2ecf20Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci /* 27288c2ecf20Sopenharmony_ci * This function can be called from a work, thus it may be possible 27298c2ecf20Sopenharmony_ci * that the chanctx_conf is removed (due to a disconnection, for 27308c2ecf20Sopenharmony_ci * example). 27318c2ecf20Sopenharmony_ci * So nothing should be done in such case. 27328c2ecf20Sopenharmony_ci */ 27338c2ecf20Sopenharmony_ci if (!chanctx_conf) 27348c2ecf20Sopenharmony_ci goto unlock; 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 27378c2ecf20Sopenharmony_ci ieee80211_recalc_smps_chanctx(local, chanctx); 27388c2ecf20Sopenharmony_ci unlock: 27398c2ecf20Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 27408c2ecf20Sopenharmony_ci} 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_civoid ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) 27438c2ecf20Sopenharmony_ci{ 27448c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 27458c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 27468c2ecf20Sopenharmony_ci struct ieee80211_chanctx *chanctx; 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, 27518c2ecf20Sopenharmony_ci lockdep_is_held(&local->chanctx_mtx)); 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!chanctx_conf)) 27548c2ecf20Sopenharmony_ci goto unlock; 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 27578c2ecf20Sopenharmony_ci ieee80211_recalc_chanctx_min_def(local, chanctx); 27588c2ecf20Sopenharmony_ci unlock: 27598c2ecf20Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 27608c2ecf20Sopenharmony_ci} 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_cisize_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) 27638c2ecf20Sopenharmony_ci{ 27648c2ecf20Sopenharmony_ci size_t pos = offset; 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci while (pos < ielen && ies[pos] != WLAN_EID_VENDOR_SPECIFIC) 27678c2ecf20Sopenharmony_ci pos += 2 + ies[pos + 1]; 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci return pos; 27708c2ecf20Sopenharmony_ci} 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_cistatic void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata, 27738c2ecf20Sopenharmony_ci int rssi_min_thold, 27748c2ecf20Sopenharmony_ci int rssi_max_thold) 27758c2ecf20Sopenharmony_ci{ 27768c2ecf20Sopenharmony_ci trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold); 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 27798c2ecf20Sopenharmony_ci return; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci /* 27828c2ecf20Sopenharmony_ci * Scale up threshold values before storing it, as the RSSI averaging 27838c2ecf20Sopenharmony_ci * algorithm uses a scaled up value as well. Change this scaling 27848c2ecf20Sopenharmony_ci * factor if the RSSI averaging algorithm changes. 27858c2ecf20Sopenharmony_ci */ 27868c2ecf20Sopenharmony_ci sdata->u.mgd.rssi_min_thold = rssi_min_thold*16; 27878c2ecf20Sopenharmony_ci sdata->u.mgd.rssi_max_thold = rssi_max_thold*16; 27888c2ecf20Sopenharmony_ci} 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_civoid ieee80211_enable_rssi_reports(struct ieee80211_vif *vif, 27918c2ecf20Sopenharmony_ci int rssi_min_thold, 27928c2ecf20Sopenharmony_ci int rssi_max_thold) 27938c2ecf20Sopenharmony_ci{ 27948c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci WARN_ON(rssi_min_thold == rssi_max_thold || 27978c2ecf20Sopenharmony_ci rssi_min_thold > rssi_max_thold); 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci _ieee80211_enable_rssi_reports(sdata, rssi_min_thold, 28008c2ecf20Sopenharmony_ci rssi_max_thold); 28018c2ecf20Sopenharmony_ci} 28028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_enable_rssi_reports); 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_civoid ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci _ieee80211_enable_rssi_reports(sdata, 0, 0); 28098c2ecf20Sopenharmony_ci} 28108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_disable_rssi_reports); 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ciu8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 28138c2ecf20Sopenharmony_ci u16 cap) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci __le16 tmp; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_HT_CAPABILITY; 28188c2ecf20Sopenharmony_ci *pos++ = sizeof(struct ieee80211_ht_cap); 28198c2ecf20Sopenharmony_ci memset(pos, 0, sizeof(struct ieee80211_ht_cap)); 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci /* capability flags */ 28228c2ecf20Sopenharmony_ci tmp = cpu_to_le16(cap); 28238c2ecf20Sopenharmony_ci memcpy(pos, &tmp, sizeof(u16)); 28248c2ecf20Sopenharmony_ci pos += sizeof(u16); 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci /* AMPDU parameters */ 28278c2ecf20Sopenharmony_ci *pos++ = ht_cap->ampdu_factor | 28288c2ecf20Sopenharmony_ci (ht_cap->ampdu_density << 28298c2ecf20Sopenharmony_ci IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci /* MCS set */ 28328c2ecf20Sopenharmony_ci memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); 28338c2ecf20Sopenharmony_ci pos += sizeof(ht_cap->mcs); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci /* extended capabilities */ 28368c2ecf20Sopenharmony_ci pos += sizeof(__le16); 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci /* BF capabilities */ 28398c2ecf20Sopenharmony_ci pos += sizeof(__le32); 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci /* antenna selection */ 28428c2ecf20Sopenharmony_ci pos += sizeof(u8); 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci return pos; 28458c2ecf20Sopenharmony_ci} 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ciu8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 28488c2ecf20Sopenharmony_ci u32 cap) 28498c2ecf20Sopenharmony_ci{ 28508c2ecf20Sopenharmony_ci __le32 tmp; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_VHT_CAPABILITY; 28538c2ecf20Sopenharmony_ci *pos++ = sizeof(struct ieee80211_vht_cap); 28548c2ecf20Sopenharmony_ci memset(pos, 0, sizeof(struct ieee80211_vht_cap)); 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci /* capability flags */ 28578c2ecf20Sopenharmony_ci tmp = cpu_to_le32(cap); 28588c2ecf20Sopenharmony_ci memcpy(pos, &tmp, sizeof(u32)); 28598c2ecf20Sopenharmony_ci pos += sizeof(u32); 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci /* VHT MCS set */ 28628c2ecf20Sopenharmony_ci memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs)); 28638c2ecf20Sopenharmony_ci pos += sizeof(vht_cap->vht_mcs); 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci return pos; 28668c2ecf20Sopenharmony_ci} 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ciu8 ieee80211_ie_len_he_cap(struct ieee80211_sub_if_data *sdata, u8 iftype) 28698c2ecf20Sopenharmony_ci{ 28708c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 28718c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 28728c2ecf20Sopenharmony_ci u8 n; 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci sband = ieee80211_get_sband(sdata); 28758c2ecf20Sopenharmony_ci if (!sband) 28768c2ecf20Sopenharmony_ci return 0; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 28798c2ecf20Sopenharmony_ci if (!he_cap) 28808c2ecf20Sopenharmony_ci return 0; 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); 28838c2ecf20Sopenharmony_ci return 2 + 1 + 28848c2ecf20Sopenharmony_ci sizeof(he_cap->he_cap_elem) + n + 28858c2ecf20Sopenharmony_ci ieee80211_he_ppe_size(he_cap->ppe_thres[0], 28868c2ecf20Sopenharmony_ci he_cap->he_cap_elem.phy_cap_info); 28878c2ecf20Sopenharmony_ci} 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ciu8 *ieee80211_ie_build_he_cap(u8 *pos, 28908c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap, 28918c2ecf20Sopenharmony_ci u8 *end) 28928c2ecf20Sopenharmony_ci{ 28938c2ecf20Sopenharmony_ci u8 n; 28948c2ecf20Sopenharmony_ci u8 ie_len; 28958c2ecf20Sopenharmony_ci u8 *orig_pos = pos; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci /* Make sure we have place for the IE */ 28988c2ecf20Sopenharmony_ci /* 28998c2ecf20Sopenharmony_ci * TODO: the 1 added is because this temporarily is under the EXTENSION 29008c2ecf20Sopenharmony_ci * IE. Get rid of it when it moves. 29018c2ecf20Sopenharmony_ci */ 29028c2ecf20Sopenharmony_ci if (!he_cap) 29038c2ecf20Sopenharmony_ci return orig_pos; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem); 29068c2ecf20Sopenharmony_ci ie_len = 2 + 1 + 29078c2ecf20Sopenharmony_ci sizeof(he_cap->he_cap_elem) + n + 29088c2ecf20Sopenharmony_ci ieee80211_he_ppe_size(he_cap->ppe_thres[0], 29098c2ecf20Sopenharmony_ci he_cap->he_cap_elem.phy_cap_info); 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci if ((end - pos) < ie_len) 29128c2ecf20Sopenharmony_ci return orig_pos; 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 29158c2ecf20Sopenharmony_ci pos++; /* We'll set the size later below */ 29168c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXT_HE_CAPABILITY; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci /* Fixed data */ 29198c2ecf20Sopenharmony_ci memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem)); 29208c2ecf20Sopenharmony_ci pos += sizeof(he_cap->he_cap_elem); 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci memcpy(pos, &he_cap->he_mcs_nss_supp, n); 29238c2ecf20Sopenharmony_ci pos += n; 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci /* Check if PPE Threshold should be present */ 29268c2ecf20Sopenharmony_ci if ((he_cap->he_cap_elem.phy_cap_info[6] & 29278c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0) 29288c2ecf20Sopenharmony_ci goto end; 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci /* 29318c2ecf20Sopenharmony_ci * Calculate how many PPET16/PPET8 pairs are to come. Algorithm: 29328c2ecf20Sopenharmony_ci * (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK) 29338c2ecf20Sopenharmony_ci */ 29348c2ecf20Sopenharmony_ci n = hweight8(he_cap->ppe_thres[0] & 29358c2ecf20Sopenharmony_ci IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); 29368c2ecf20Sopenharmony_ci n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >> 29378c2ecf20Sopenharmony_ci IEEE80211_PPE_THRES_NSS_POS)); 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci /* 29408c2ecf20Sopenharmony_ci * Each pair is 6 bits, and we need to add the 7 "header" bits to the 29418c2ecf20Sopenharmony_ci * total size. 29428c2ecf20Sopenharmony_ci */ 29438c2ecf20Sopenharmony_ci n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7; 29448c2ecf20Sopenharmony_ci n = DIV_ROUND_UP(n, 8); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci /* Copy PPE Thresholds */ 29478c2ecf20Sopenharmony_ci memcpy(pos, &he_cap->ppe_thres, n); 29488c2ecf20Sopenharmony_ci pos += n; 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_ciend: 29518c2ecf20Sopenharmony_ci orig_pos[1] = (pos - orig_pos) - 2; 29528c2ecf20Sopenharmony_ci return pos; 29538c2ecf20Sopenharmony_ci} 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_civoid ieee80211_ie_build_he_6ghz_cap(struct ieee80211_sub_if_data *sdata, 29568c2ecf20Sopenharmony_ci struct sk_buff *skb) 29578c2ecf20Sopenharmony_ci{ 29588c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 29598c2ecf20Sopenharmony_ci const struct ieee80211_sband_iftype_data *iftd; 29608c2ecf20Sopenharmony_ci enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 29618c2ecf20Sopenharmony_ci u8 *pos; 29628c2ecf20Sopenharmony_ci u16 cap; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci sband = ieee80211_get_sband(sdata); 29658c2ecf20Sopenharmony_ci if (!sband) 29668c2ecf20Sopenharmony_ci return; 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci iftd = ieee80211_get_sband_iftype_data(sband, iftype); 29698c2ecf20Sopenharmony_ci if (WARN_ON(!iftd)) 29708c2ecf20Sopenharmony_ci return; 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_ci /* Check for device HE 6 GHz capability before adding element */ 29738c2ecf20Sopenharmony_ci if (!iftd->he_6ghz_capa.capa) 29748c2ecf20Sopenharmony_ci return; 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_ci cap = le16_to_cpu(iftd->he_6ghz_capa.capa); 29778c2ecf20Sopenharmony_ci cap &= ~IEEE80211_HE_6GHZ_CAP_SM_PS; 29788c2ecf20Sopenharmony_ci 29798c2ecf20Sopenharmony_ci switch (sdata->smps_mode) { 29808c2ecf20Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 29818c2ecf20Sopenharmony_ci case IEEE80211_SMPS_NUM_MODES: 29828c2ecf20Sopenharmony_ci WARN_ON(1); 29838c2ecf20Sopenharmony_ci fallthrough; 29848c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 29858c2ecf20Sopenharmony_ci cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DISABLED, 29868c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_SM_PS); 29878c2ecf20Sopenharmony_ci break; 29888c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 29898c2ecf20Sopenharmony_ci cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_STATIC, 29908c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_SM_PS); 29918c2ecf20Sopenharmony_ci break; 29928c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 29938c2ecf20Sopenharmony_ci cap |= u16_encode_bits(WLAN_HT_CAP_SM_PS_DYNAMIC, 29948c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_CAP_SM_PS); 29958c2ecf20Sopenharmony_ci break; 29968c2ecf20Sopenharmony_ci } 29978c2ecf20Sopenharmony_ci 29988c2ecf20Sopenharmony_ci pos = skb_put(skb, 2 + 1 + sizeof(cap)); 29998c2ecf20Sopenharmony_ci ieee80211_write_he_6ghz_cap(pos, cpu_to_le16(cap), 30008c2ecf20Sopenharmony_ci pos + 2 + 1 + sizeof(cap)); 30018c2ecf20Sopenharmony_ci} 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ciu8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, 30048c2ecf20Sopenharmony_ci const struct cfg80211_chan_def *chandef, 30058c2ecf20Sopenharmony_ci u16 prot_mode, bool rifs_mode) 30068c2ecf20Sopenharmony_ci{ 30078c2ecf20Sopenharmony_ci struct ieee80211_ht_operation *ht_oper; 30088c2ecf20Sopenharmony_ci /* Build HT Information */ 30098c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_HT_OPERATION; 30108c2ecf20Sopenharmony_ci *pos++ = sizeof(struct ieee80211_ht_operation); 30118c2ecf20Sopenharmony_ci ht_oper = (struct ieee80211_ht_operation *)pos; 30128c2ecf20Sopenharmony_ci ht_oper->primary_chan = ieee80211_frequency_to_channel( 30138c2ecf20Sopenharmony_ci chandef->chan->center_freq); 30148c2ecf20Sopenharmony_ci switch (chandef->width) { 30158c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 30168c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 30178c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 30188c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 30198c2ecf20Sopenharmony_ci if (chandef->center_freq1 > chandef->chan->center_freq) 30208c2ecf20Sopenharmony_ci ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 30218c2ecf20Sopenharmony_ci else 30228c2ecf20Sopenharmony_ci ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 30238c2ecf20Sopenharmony_ci break; 30248c2ecf20Sopenharmony_ci default: 30258c2ecf20Sopenharmony_ci ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; 30268c2ecf20Sopenharmony_ci break; 30278c2ecf20Sopenharmony_ci } 30288c2ecf20Sopenharmony_ci if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && 30298c2ecf20Sopenharmony_ci chandef->width != NL80211_CHAN_WIDTH_20_NOHT && 30308c2ecf20Sopenharmony_ci chandef->width != NL80211_CHAN_WIDTH_20) 30318c2ecf20Sopenharmony_ci ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci if (rifs_mode) 30348c2ecf20Sopenharmony_ci ht_oper->ht_param |= IEEE80211_HT_PARAM_RIFS_MODE; 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci ht_oper->operation_mode = cpu_to_le16(prot_mode); 30378c2ecf20Sopenharmony_ci ht_oper->stbc_param = 0x0000; 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_ci /* It seems that Basic MCS set and Supported MCS set 30408c2ecf20Sopenharmony_ci are identical for the first 10 bytes */ 30418c2ecf20Sopenharmony_ci memset(&ht_oper->basic_set, 0, 16); 30428c2ecf20Sopenharmony_ci memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10); 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci return pos + sizeof(struct ieee80211_ht_operation); 30458c2ecf20Sopenharmony_ci} 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_civoid ieee80211_ie_build_wide_bw_cs(u8 *pos, 30488c2ecf20Sopenharmony_ci const struct cfg80211_chan_def *chandef) 30498c2ecf20Sopenharmony_ci{ 30508c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_WIDE_BW_CHANNEL_SWITCH; /* EID */ 30518c2ecf20Sopenharmony_ci *pos++ = 3; /* IE length */ 30528c2ecf20Sopenharmony_ci /* New channel width */ 30538c2ecf20Sopenharmony_ci switch (chandef->width) { 30548c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 30558c2ecf20Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_80MHZ; 30568c2ecf20Sopenharmony_ci break; 30578c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 30588c2ecf20Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_160MHZ; 30598c2ecf20Sopenharmony_ci break; 30608c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 30618c2ecf20Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_80P80MHZ; 30628c2ecf20Sopenharmony_ci break; 30638c2ecf20Sopenharmony_ci default: 30648c2ecf20Sopenharmony_ci *pos++ = IEEE80211_VHT_CHANWIDTH_USE_HT; 30658c2ecf20Sopenharmony_ci } 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci /* new center frequency segment 0 */ 30688c2ecf20Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(chandef->center_freq1); 30698c2ecf20Sopenharmony_ci /* new center frequency segment 1 */ 30708c2ecf20Sopenharmony_ci if (chandef->center_freq2) 30718c2ecf20Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(chandef->center_freq2); 30728c2ecf20Sopenharmony_ci else 30738c2ecf20Sopenharmony_ci *pos++ = 0; 30748c2ecf20Sopenharmony_ci} 30758c2ecf20Sopenharmony_ci 30768c2ecf20Sopenharmony_ciu8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, 30778c2ecf20Sopenharmony_ci const struct cfg80211_chan_def *chandef) 30788c2ecf20Sopenharmony_ci{ 30798c2ecf20Sopenharmony_ci struct ieee80211_vht_operation *vht_oper; 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_VHT_OPERATION; 30828c2ecf20Sopenharmony_ci *pos++ = sizeof(struct ieee80211_vht_operation); 30838c2ecf20Sopenharmony_ci vht_oper = (struct ieee80211_vht_operation *)pos; 30848c2ecf20Sopenharmony_ci vht_oper->center_freq_seg0_idx = ieee80211_frequency_to_channel( 30858c2ecf20Sopenharmony_ci chandef->center_freq1); 30868c2ecf20Sopenharmony_ci if (chandef->center_freq2) 30878c2ecf20Sopenharmony_ci vht_oper->center_freq_seg1_idx = 30888c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq2); 30898c2ecf20Sopenharmony_ci else 30908c2ecf20Sopenharmony_ci vht_oper->center_freq_seg1_idx = 0x00; 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci switch (chandef->width) { 30938c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 30948c2ecf20Sopenharmony_ci /* 30958c2ecf20Sopenharmony_ci * Convert 160 MHz channel width to new style as interop 30968c2ecf20Sopenharmony_ci * workaround. 30978c2ecf20Sopenharmony_ci */ 30988c2ecf20Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 30998c2ecf20Sopenharmony_ci vht_oper->center_freq_seg1_idx = vht_oper->center_freq_seg0_idx; 31008c2ecf20Sopenharmony_ci if (chandef->chan->center_freq < chandef->center_freq1) 31018c2ecf20Sopenharmony_ci vht_oper->center_freq_seg0_idx -= 8; 31028c2ecf20Sopenharmony_ci else 31038c2ecf20Sopenharmony_ci vht_oper->center_freq_seg0_idx += 8; 31048c2ecf20Sopenharmony_ci break; 31058c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 31068c2ecf20Sopenharmony_ci /* 31078c2ecf20Sopenharmony_ci * Convert 80+80 MHz channel width to new style as interop 31088c2ecf20Sopenharmony_ci * workaround. 31098c2ecf20Sopenharmony_ci */ 31108c2ecf20Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 31118c2ecf20Sopenharmony_ci break; 31128c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 31138c2ecf20Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; 31148c2ecf20Sopenharmony_ci break; 31158c2ecf20Sopenharmony_ci default: 31168c2ecf20Sopenharmony_ci vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; 31178c2ecf20Sopenharmony_ci break; 31188c2ecf20Sopenharmony_ci } 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci /* don't require special VHT peer rates */ 31218c2ecf20Sopenharmony_ci vht_oper->basic_mcs_set = cpu_to_le16(0xffff); 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ci return pos + sizeof(struct ieee80211_vht_operation); 31248c2ecf20Sopenharmony_ci} 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ciu8 *ieee80211_ie_build_he_oper(u8 *pos, struct cfg80211_chan_def *chandef) 31278c2ecf20Sopenharmony_ci{ 31288c2ecf20Sopenharmony_ci struct ieee80211_he_operation *he_oper; 31298c2ecf20Sopenharmony_ci struct ieee80211_he_6ghz_oper *he_6ghz_op; 31308c2ecf20Sopenharmony_ci u32 he_oper_params; 31318c2ecf20Sopenharmony_ci u8 ie_len = 1 + sizeof(struct ieee80211_he_operation); 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 31348c2ecf20Sopenharmony_ci ie_len += sizeof(struct ieee80211_he_6ghz_oper); 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXTENSION; 31378c2ecf20Sopenharmony_ci *pos++ = ie_len; 31388c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXT_HE_OPERATION; 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci he_oper_params = 0; 31418c2ecf20Sopenharmony_ci he_oper_params |= u32_encode_bits(1023, /* disabled */ 31428c2ecf20Sopenharmony_ci IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK); 31438c2ecf20Sopenharmony_ci he_oper_params |= u32_encode_bits(1, 31448c2ecf20Sopenharmony_ci IEEE80211_HE_OPERATION_ER_SU_DISABLE); 31458c2ecf20Sopenharmony_ci he_oper_params |= u32_encode_bits(1, 31468c2ecf20Sopenharmony_ci IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED); 31478c2ecf20Sopenharmony_ci if (chandef->chan->band == NL80211_BAND_6GHZ) 31488c2ecf20Sopenharmony_ci he_oper_params |= u32_encode_bits(1, 31498c2ecf20Sopenharmony_ci IEEE80211_HE_OPERATION_6GHZ_OP_INFO); 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci he_oper = (struct ieee80211_he_operation *)pos; 31528c2ecf20Sopenharmony_ci he_oper->he_oper_params = cpu_to_le32(he_oper_params); 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci /* don't require special HE peer rates */ 31558c2ecf20Sopenharmony_ci he_oper->he_mcs_nss_set = cpu_to_le16(0xffff); 31568c2ecf20Sopenharmony_ci pos += sizeof(struct ieee80211_he_operation); 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_6GHZ) 31598c2ecf20Sopenharmony_ci goto out; 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci /* TODO add VHT operational */ 31628c2ecf20Sopenharmony_ci he_6ghz_op = (struct ieee80211_he_6ghz_oper *)pos; 31638c2ecf20Sopenharmony_ci he_6ghz_op->minrate = 6; /* 6 Mbps */ 31648c2ecf20Sopenharmony_ci he_6ghz_op->primary = 31658c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(chandef->chan->center_freq); 31668c2ecf20Sopenharmony_ci he_6ghz_op->ccfs0 = 31678c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq1); 31688c2ecf20Sopenharmony_ci if (chandef->center_freq2) 31698c2ecf20Sopenharmony_ci he_6ghz_op->ccfs1 = 31708c2ecf20Sopenharmony_ci ieee80211_frequency_to_channel(chandef->center_freq2); 31718c2ecf20Sopenharmony_ci else 31728c2ecf20Sopenharmony_ci he_6ghz_op->ccfs1 = 0; 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci switch (chandef->width) { 31758c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 31768c2ecf20Sopenharmony_ci /* Convert 160 MHz channel width to new style as interop 31778c2ecf20Sopenharmony_ci * workaround. 31788c2ecf20Sopenharmony_ci */ 31798c2ecf20Sopenharmony_ci he_6ghz_op->control = 31808c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; 31818c2ecf20Sopenharmony_ci he_6ghz_op->ccfs1 = he_6ghz_op->ccfs0; 31828c2ecf20Sopenharmony_ci if (chandef->chan->center_freq < chandef->center_freq1) 31838c2ecf20Sopenharmony_ci he_6ghz_op->ccfs0 -= 8; 31848c2ecf20Sopenharmony_ci else 31858c2ecf20Sopenharmony_ci he_6ghz_op->ccfs0 += 8; 31868c2ecf20Sopenharmony_ci fallthrough; 31878c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 31888c2ecf20Sopenharmony_ci he_6ghz_op->control = 31898c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ; 31908c2ecf20Sopenharmony_ci break; 31918c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 31928c2ecf20Sopenharmony_ci he_6ghz_op->control = 31938c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ; 31948c2ecf20Sopenharmony_ci break; 31958c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 31968c2ecf20Sopenharmony_ci he_6ghz_op->control = 31978c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ; 31988c2ecf20Sopenharmony_ci break; 31998c2ecf20Sopenharmony_ci default: 32008c2ecf20Sopenharmony_ci he_6ghz_op->control = 32018c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ; 32028c2ecf20Sopenharmony_ci break; 32038c2ecf20Sopenharmony_ci } 32048c2ecf20Sopenharmony_ci 32058c2ecf20Sopenharmony_ci pos += sizeof(struct ieee80211_he_6ghz_oper); 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ciout: 32088c2ecf20Sopenharmony_ci return pos; 32098c2ecf20Sopenharmony_ci} 32108c2ecf20Sopenharmony_ci 32118c2ecf20Sopenharmony_cibool ieee80211_chandef_ht_oper(const struct ieee80211_ht_operation *ht_oper, 32128c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef) 32138c2ecf20Sopenharmony_ci{ 32148c2ecf20Sopenharmony_ci enum nl80211_channel_type channel_type; 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci if (!ht_oper) 32178c2ecf20Sopenharmony_ci return false; 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { 32208c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_NONE: 32218c2ecf20Sopenharmony_ci channel_type = NL80211_CHAN_HT20; 32228c2ecf20Sopenharmony_ci break; 32238c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: 32248c2ecf20Sopenharmony_ci channel_type = NL80211_CHAN_HT40PLUS; 32258c2ecf20Sopenharmony_ci break; 32268c2ecf20Sopenharmony_ci case IEEE80211_HT_PARAM_CHA_SEC_BELOW: 32278c2ecf20Sopenharmony_ci channel_type = NL80211_CHAN_HT40MINUS; 32288c2ecf20Sopenharmony_ci break; 32298c2ecf20Sopenharmony_ci default: 32308c2ecf20Sopenharmony_ci channel_type = NL80211_CHAN_NO_HT; 32318c2ecf20Sopenharmony_ci return false; 32328c2ecf20Sopenharmony_ci } 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci cfg80211_chandef_create(chandef, chandef->chan, channel_type); 32358c2ecf20Sopenharmony_ci return true; 32368c2ecf20Sopenharmony_ci} 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_cibool ieee80211_chandef_vht_oper(struct ieee80211_hw *hw, u32 vht_cap_info, 32398c2ecf20Sopenharmony_ci const struct ieee80211_vht_operation *oper, 32408c2ecf20Sopenharmony_ci const struct ieee80211_ht_operation *htop, 32418c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef) 32428c2ecf20Sopenharmony_ci{ 32438c2ecf20Sopenharmony_ci struct cfg80211_chan_def new = *chandef; 32448c2ecf20Sopenharmony_ci int cf0, cf1; 32458c2ecf20Sopenharmony_ci int ccfs0, ccfs1, ccfs2; 32468c2ecf20Sopenharmony_ci int ccf0, ccf1; 32478c2ecf20Sopenharmony_ci u32 vht_cap; 32488c2ecf20Sopenharmony_ci bool support_80_80 = false; 32498c2ecf20Sopenharmony_ci bool support_160 = false; 32508c2ecf20Sopenharmony_ci u8 ext_nss_bw_supp = u32_get_bits(vht_cap_info, 32518c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); 32528c2ecf20Sopenharmony_ci u8 supp_chwidth = u32_get_bits(vht_cap_info, 32538c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK); 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci if (!oper || !htop) 32568c2ecf20Sopenharmony_ci return false; 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci vht_cap = hw->wiphy->bands[chandef->chan->band]->vht_cap.cap; 32598c2ecf20Sopenharmony_ci support_160 = (vht_cap & (IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK | 32608c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)); 32618c2ecf20Sopenharmony_ci support_80_80 = ((vht_cap & 32628c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) || 32638c2ecf20Sopenharmony_ci (vht_cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ && 32648c2ecf20Sopenharmony_ci vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) || 32658c2ecf20Sopenharmony_ci ((vht_cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) >> 32668c2ecf20Sopenharmony_ci IEEE80211_VHT_CAP_EXT_NSS_BW_SHIFT > 1)); 32678c2ecf20Sopenharmony_ci ccfs0 = oper->center_freq_seg0_idx; 32688c2ecf20Sopenharmony_ci ccfs1 = oper->center_freq_seg1_idx; 32698c2ecf20Sopenharmony_ci ccfs2 = (le16_to_cpu(htop->operation_mode) & 32708c2ecf20Sopenharmony_ci IEEE80211_HT_OP_MODE_CCFS2_MASK) 32718c2ecf20Sopenharmony_ci >> IEEE80211_HT_OP_MODE_CCFS2_SHIFT; 32728c2ecf20Sopenharmony_ci 32738c2ecf20Sopenharmony_ci ccf0 = ccfs0; 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_ci /* if not supported, parse as though we didn't understand it */ 32768c2ecf20Sopenharmony_ci if (!ieee80211_hw_check(hw, SUPPORTS_VHT_EXT_NSS_BW)) 32778c2ecf20Sopenharmony_ci ext_nss_bw_supp = 0; 32788c2ecf20Sopenharmony_ci 32798c2ecf20Sopenharmony_ci /* 32808c2ecf20Sopenharmony_ci * Cf. IEEE 802.11 Table 9-250 32818c2ecf20Sopenharmony_ci * 32828c2ecf20Sopenharmony_ci * We really just consider that because it's inefficient to connect 32838c2ecf20Sopenharmony_ci * at a higher bandwidth than we'll actually be able to use. 32848c2ecf20Sopenharmony_ci */ 32858c2ecf20Sopenharmony_ci switch ((supp_chwidth << 4) | ext_nss_bw_supp) { 32868c2ecf20Sopenharmony_ci default: 32878c2ecf20Sopenharmony_ci case 0x00: 32888c2ecf20Sopenharmony_ci ccf1 = 0; 32898c2ecf20Sopenharmony_ci support_160 = false; 32908c2ecf20Sopenharmony_ci support_80_80 = false; 32918c2ecf20Sopenharmony_ci break; 32928c2ecf20Sopenharmony_ci case 0x01: 32938c2ecf20Sopenharmony_ci support_80_80 = false; 32948c2ecf20Sopenharmony_ci fallthrough; 32958c2ecf20Sopenharmony_ci case 0x02: 32968c2ecf20Sopenharmony_ci case 0x03: 32978c2ecf20Sopenharmony_ci ccf1 = ccfs2; 32988c2ecf20Sopenharmony_ci break; 32998c2ecf20Sopenharmony_ci case 0x10: 33008c2ecf20Sopenharmony_ci ccf1 = ccfs1; 33018c2ecf20Sopenharmony_ci break; 33028c2ecf20Sopenharmony_ci case 0x11: 33038c2ecf20Sopenharmony_ci case 0x12: 33048c2ecf20Sopenharmony_ci if (!ccfs1) 33058c2ecf20Sopenharmony_ci ccf1 = ccfs2; 33068c2ecf20Sopenharmony_ci else 33078c2ecf20Sopenharmony_ci ccf1 = ccfs1; 33088c2ecf20Sopenharmony_ci break; 33098c2ecf20Sopenharmony_ci case 0x13: 33108c2ecf20Sopenharmony_ci case 0x20: 33118c2ecf20Sopenharmony_ci case 0x23: 33128c2ecf20Sopenharmony_ci ccf1 = ccfs1; 33138c2ecf20Sopenharmony_ci break; 33148c2ecf20Sopenharmony_ci } 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci cf0 = ieee80211_channel_to_frequency(ccf0, chandef->chan->band); 33178c2ecf20Sopenharmony_ci cf1 = ieee80211_channel_to_frequency(ccf1, chandef->chan->band); 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci switch (oper->chan_width) { 33208c2ecf20Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_USE_HT: 33218c2ecf20Sopenharmony_ci /* just use HT information directly */ 33228c2ecf20Sopenharmony_ci break; 33238c2ecf20Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_80MHZ: 33248c2ecf20Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_80; 33258c2ecf20Sopenharmony_ci new.center_freq1 = cf0; 33268c2ecf20Sopenharmony_ci /* If needed, adjust based on the newer interop workaround. */ 33278c2ecf20Sopenharmony_ci if (ccf1) { 33288c2ecf20Sopenharmony_ci unsigned int diff; 33298c2ecf20Sopenharmony_ci 33308c2ecf20Sopenharmony_ci diff = abs(ccf1 - ccf0); 33318c2ecf20Sopenharmony_ci if ((diff == 8) && support_160) { 33328c2ecf20Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_160; 33338c2ecf20Sopenharmony_ci new.center_freq1 = cf1; 33348c2ecf20Sopenharmony_ci } else if ((diff > 8) && support_80_80) { 33358c2ecf20Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_80P80; 33368c2ecf20Sopenharmony_ci new.center_freq2 = cf1; 33378c2ecf20Sopenharmony_ci } 33388c2ecf20Sopenharmony_ci } 33398c2ecf20Sopenharmony_ci break; 33408c2ecf20Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_160MHZ: 33418c2ecf20Sopenharmony_ci /* deprecated encoding */ 33428c2ecf20Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_160; 33438c2ecf20Sopenharmony_ci new.center_freq1 = cf0; 33448c2ecf20Sopenharmony_ci break; 33458c2ecf20Sopenharmony_ci case IEEE80211_VHT_CHANWIDTH_80P80MHZ: 33468c2ecf20Sopenharmony_ci /* deprecated encoding */ 33478c2ecf20Sopenharmony_ci new.width = NL80211_CHAN_WIDTH_80P80; 33488c2ecf20Sopenharmony_ci new.center_freq1 = cf0; 33498c2ecf20Sopenharmony_ci new.center_freq2 = cf1; 33508c2ecf20Sopenharmony_ci break; 33518c2ecf20Sopenharmony_ci default: 33528c2ecf20Sopenharmony_ci return false; 33538c2ecf20Sopenharmony_ci } 33548c2ecf20Sopenharmony_ci 33558c2ecf20Sopenharmony_ci if (!cfg80211_chandef_valid(&new)) 33568c2ecf20Sopenharmony_ci return false; 33578c2ecf20Sopenharmony_ci 33588c2ecf20Sopenharmony_ci *chandef = new; 33598c2ecf20Sopenharmony_ci return true; 33608c2ecf20Sopenharmony_ci} 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_cibool ieee80211_chandef_he_6ghz_oper(struct ieee80211_sub_if_data *sdata, 33638c2ecf20Sopenharmony_ci const struct ieee80211_he_operation *he_oper, 33648c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef) 33658c2ecf20Sopenharmony_ci{ 33668c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 33678c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 33688c2ecf20Sopenharmony_ci enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif); 33698c2ecf20Sopenharmony_ci const struct ieee80211_sta_he_cap *he_cap; 33708c2ecf20Sopenharmony_ci struct cfg80211_chan_def he_chandef = *chandef; 33718c2ecf20Sopenharmony_ci const struct ieee80211_he_6ghz_oper *he_6ghz_oper; 33728c2ecf20Sopenharmony_ci bool support_80_80, support_160; 33738c2ecf20Sopenharmony_ci u8 he_phy_cap; 33748c2ecf20Sopenharmony_ci u32 freq; 33758c2ecf20Sopenharmony_ci 33768c2ecf20Sopenharmony_ci if (chandef->chan->band != NL80211_BAND_6GHZ) 33778c2ecf20Sopenharmony_ci return true; 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[NL80211_BAND_6GHZ]; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci he_cap = ieee80211_get_he_iftype_cap(sband, iftype); 33828c2ecf20Sopenharmony_ci if (!he_cap) { 33838c2ecf20Sopenharmony_ci sdata_info(sdata, "Missing iftype sband data/HE cap"); 33848c2ecf20Sopenharmony_ci return false; 33858c2ecf20Sopenharmony_ci } 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci he_phy_cap = he_cap->he_cap_elem.phy_cap_info[0]; 33888c2ecf20Sopenharmony_ci support_160 = 33898c2ecf20Sopenharmony_ci he_phy_cap & 33908c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; 33918c2ecf20Sopenharmony_ci support_80_80 = 33928c2ecf20Sopenharmony_ci he_phy_cap & 33938c2ecf20Sopenharmony_ci IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; 33948c2ecf20Sopenharmony_ci 33958c2ecf20Sopenharmony_ci if (!he_oper) { 33968c2ecf20Sopenharmony_ci sdata_info(sdata, 33978c2ecf20Sopenharmony_ci "HE is not advertised on (on %d MHz), expect issues\n", 33988c2ecf20Sopenharmony_ci chandef->chan->center_freq); 33998c2ecf20Sopenharmony_ci return false; 34008c2ecf20Sopenharmony_ci } 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper); 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci if (!he_6ghz_oper) { 34058c2ecf20Sopenharmony_ci sdata_info(sdata, 34068c2ecf20Sopenharmony_ci "HE 6GHz operation missing (on %d MHz), expect issues\n", 34078c2ecf20Sopenharmony_ci chandef->chan->center_freq); 34088c2ecf20Sopenharmony_ci return false; 34098c2ecf20Sopenharmony_ci } 34108c2ecf20Sopenharmony_ci 34118c2ecf20Sopenharmony_ci freq = ieee80211_channel_to_frequency(he_6ghz_oper->primary, 34128c2ecf20Sopenharmony_ci NL80211_BAND_6GHZ); 34138c2ecf20Sopenharmony_ci he_chandef.chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci switch (u8_get_bits(he_6ghz_oper->control, 34168c2ecf20Sopenharmony_ci IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH)) { 34178c2ecf20Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_20MHZ: 34188c2ecf20Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_20; 34198c2ecf20Sopenharmony_ci break; 34208c2ecf20Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_40MHZ: 34218c2ecf20Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_40; 34228c2ecf20Sopenharmony_ci break; 34238c2ecf20Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_80MHZ: 34248c2ecf20Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_80; 34258c2ecf20Sopenharmony_ci break; 34268c2ecf20Sopenharmony_ci case IEEE80211_HE_6GHZ_OPER_CTRL_CHANWIDTH_160MHZ: 34278c2ecf20Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_80; 34288c2ecf20Sopenharmony_ci if (!he_6ghz_oper->ccfs1) 34298c2ecf20Sopenharmony_ci break; 34308c2ecf20Sopenharmony_ci if (abs(he_6ghz_oper->ccfs1 - he_6ghz_oper->ccfs0) == 8) { 34318c2ecf20Sopenharmony_ci if (support_160) 34328c2ecf20Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_160; 34338c2ecf20Sopenharmony_ci } else { 34348c2ecf20Sopenharmony_ci if (support_80_80) 34358c2ecf20Sopenharmony_ci he_chandef.width = NL80211_CHAN_WIDTH_80P80; 34368c2ecf20Sopenharmony_ci } 34378c2ecf20Sopenharmony_ci break; 34388c2ecf20Sopenharmony_ci } 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_ci if (he_chandef.width == NL80211_CHAN_WIDTH_160) { 34418c2ecf20Sopenharmony_ci he_chandef.center_freq1 = 34428c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, 34438c2ecf20Sopenharmony_ci NL80211_BAND_6GHZ); 34448c2ecf20Sopenharmony_ci } else { 34458c2ecf20Sopenharmony_ci he_chandef.center_freq1 = 34468c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(he_6ghz_oper->ccfs0, 34478c2ecf20Sopenharmony_ci NL80211_BAND_6GHZ); 34488c2ecf20Sopenharmony_ci if (support_80_80 || support_160) 34498c2ecf20Sopenharmony_ci he_chandef.center_freq2 = 34508c2ecf20Sopenharmony_ci ieee80211_channel_to_frequency(he_6ghz_oper->ccfs1, 34518c2ecf20Sopenharmony_ci NL80211_BAND_6GHZ); 34528c2ecf20Sopenharmony_ci } 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci if (!cfg80211_chandef_valid(&he_chandef)) { 34558c2ecf20Sopenharmony_ci sdata_info(sdata, 34568c2ecf20Sopenharmony_ci "HE 6GHz operation resulted in invalid chandef: %d MHz/%d/%d MHz/%d MHz\n", 34578c2ecf20Sopenharmony_ci he_chandef.chan ? he_chandef.chan->center_freq : 0, 34588c2ecf20Sopenharmony_ci he_chandef.width, 34598c2ecf20Sopenharmony_ci he_chandef.center_freq1, 34608c2ecf20Sopenharmony_ci he_chandef.center_freq2); 34618c2ecf20Sopenharmony_ci return false; 34628c2ecf20Sopenharmony_ci } 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci *chandef = he_chandef; 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci return true; 34678c2ecf20Sopenharmony_ci} 34688c2ecf20Sopenharmony_ci 34698c2ecf20Sopenharmony_cibool ieee80211_chandef_s1g_oper(const struct ieee80211_s1g_oper_ie *oper, 34708c2ecf20Sopenharmony_ci struct cfg80211_chan_def *chandef) 34718c2ecf20Sopenharmony_ci{ 34728c2ecf20Sopenharmony_ci u32 oper_freq; 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci if (!oper) 34758c2ecf20Sopenharmony_ci return false; 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci switch (FIELD_GET(S1G_OPER_CH_WIDTH_OPER, oper->ch_width)) { 34788c2ecf20Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_1MHZ: 34798c2ecf20Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_1; 34808c2ecf20Sopenharmony_ci break; 34818c2ecf20Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_2MHZ: 34828c2ecf20Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_2; 34838c2ecf20Sopenharmony_ci break; 34848c2ecf20Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_4MHZ: 34858c2ecf20Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_4; 34868c2ecf20Sopenharmony_ci break; 34878c2ecf20Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_8MHZ: 34888c2ecf20Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_8; 34898c2ecf20Sopenharmony_ci break; 34908c2ecf20Sopenharmony_ci case IEEE80211_S1G_CHANWIDTH_16MHZ: 34918c2ecf20Sopenharmony_ci chandef->width = NL80211_CHAN_WIDTH_16; 34928c2ecf20Sopenharmony_ci break; 34938c2ecf20Sopenharmony_ci default: 34948c2ecf20Sopenharmony_ci return false; 34958c2ecf20Sopenharmony_ci } 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci oper_freq = ieee80211_channel_to_freq_khz(oper->oper_ch, 34988c2ecf20Sopenharmony_ci NL80211_BAND_S1GHZ); 34998c2ecf20Sopenharmony_ci chandef->center_freq1 = KHZ_TO_MHZ(oper_freq); 35008c2ecf20Sopenharmony_ci chandef->freq1_offset = oper_freq % 1000; 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci return true; 35038c2ecf20Sopenharmony_ci} 35048c2ecf20Sopenharmony_ci 35058c2ecf20Sopenharmony_ciint ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, 35068c2ecf20Sopenharmony_ci const struct ieee80211_supported_band *sband, 35078c2ecf20Sopenharmony_ci const u8 *srates, int srates_len, u32 *rates) 35088c2ecf20Sopenharmony_ci{ 35098c2ecf20Sopenharmony_ci u32 rate_flags = ieee80211_chandef_rate_flags(chandef); 35108c2ecf20Sopenharmony_ci int shift = ieee80211_chandef_get_shift(chandef); 35118c2ecf20Sopenharmony_ci struct ieee80211_rate *br; 35128c2ecf20Sopenharmony_ci int brate, rate, i, j, count = 0; 35138c2ecf20Sopenharmony_ci 35148c2ecf20Sopenharmony_ci *rates = 0; 35158c2ecf20Sopenharmony_ci 35168c2ecf20Sopenharmony_ci for (i = 0; i < srates_len; i++) { 35178c2ecf20Sopenharmony_ci rate = srates[i] & 0x7f; 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci for (j = 0; j < sband->n_bitrates; j++) { 35208c2ecf20Sopenharmony_ci br = &sband->bitrates[j]; 35218c2ecf20Sopenharmony_ci if ((rate_flags & br->flags) != rate_flags) 35228c2ecf20Sopenharmony_ci continue; 35238c2ecf20Sopenharmony_ci 35248c2ecf20Sopenharmony_ci brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5); 35258c2ecf20Sopenharmony_ci if (brate == rate) { 35268c2ecf20Sopenharmony_ci *rates |= BIT(j); 35278c2ecf20Sopenharmony_ci count++; 35288c2ecf20Sopenharmony_ci break; 35298c2ecf20Sopenharmony_ci } 35308c2ecf20Sopenharmony_ci } 35318c2ecf20Sopenharmony_ci } 35328c2ecf20Sopenharmony_ci return count; 35338c2ecf20Sopenharmony_ci} 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ciint ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, 35368c2ecf20Sopenharmony_ci struct sk_buff *skb, bool need_basic, 35378c2ecf20Sopenharmony_ci enum nl80211_band band) 35388c2ecf20Sopenharmony_ci{ 35398c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 35408c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 35418c2ecf20Sopenharmony_ci int rate, shift; 35428c2ecf20Sopenharmony_ci u8 i, rates, *pos; 35438c2ecf20Sopenharmony_ci u32 basic_rates = sdata->vif.bss_conf.basic_rates; 35448c2ecf20Sopenharmony_ci u32 rate_flags; 35458c2ecf20Sopenharmony_ci 35468c2ecf20Sopenharmony_ci shift = ieee80211_vif_get_shift(&sdata->vif); 35478c2ecf20Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 35488c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 35498c2ecf20Sopenharmony_ci rates = 0; 35508c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 35518c2ecf20Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 35528c2ecf20Sopenharmony_ci continue; 35538c2ecf20Sopenharmony_ci rates++; 35548c2ecf20Sopenharmony_ci } 35558c2ecf20Sopenharmony_ci if (rates > 8) 35568c2ecf20Sopenharmony_ci rates = 8; 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_ci if (skb_tailroom(skb) < rates + 2) 35598c2ecf20Sopenharmony_ci return -ENOMEM; 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci pos = skb_put(skb, rates + 2); 35628c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_SUPP_RATES; 35638c2ecf20Sopenharmony_ci *pos++ = rates; 35648c2ecf20Sopenharmony_ci for (i = 0; i < rates; i++) { 35658c2ecf20Sopenharmony_ci u8 basic = 0; 35668c2ecf20Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 35678c2ecf20Sopenharmony_ci continue; 35688c2ecf20Sopenharmony_ci 35698c2ecf20Sopenharmony_ci if (need_basic && basic_rates & BIT(i)) 35708c2ecf20Sopenharmony_ci basic = 0x80; 35718c2ecf20Sopenharmony_ci rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 35728c2ecf20Sopenharmony_ci 5 * (1 << shift)); 35738c2ecf20Sopenharmony_ci *pos++ = basic | (u8) rate; 35748c2ecf20Sopenharmony_ci } 35758c2ecf20Sopenharmony_ci 35768c2ecf20Sopenharmony_ci return 0; 35778c2ecf20Sopenharmony_ci} 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ciint ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, 35808c2ecf20Sopenharmony_ci struct sk_buff *skb, bool need_basic, 35818c2ecf20Sopenharmony_ci enum nl80211_band band) 35828c2ecf20Sopenharmony_ci{ 35838c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 35848c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 35858c2ecf20Sopenharmony_ci int rate, shift; 35868c2ecf20Sopenharmony_ci u8 i, exrates, *pos; 35878c2ecf20Sopenharmony_ci u32 basic_rates = sdata->vif.bss_conf.basic_rates; 35888c2ecf20Sopenharmony_ci u32 rate_flags; 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef); 35918c2ecf20Sopenharmony_ci shift = ieee80211_vif_get_shift(&sdata->vif); 35928c2ecf20Sopenharmony_ci 35938c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[band]; 35948c2ecf20Sopenharmony_ci exrates = 0; 35958c2ecf20Sopenharmony_ci for (i = 0; i < sband->n_bitrates; i++) { 35968c2ecf20Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) != rate_flags) 35978c2ecf20Sopenharmony_ci continue; 35988c2ecf20Sopenharmony_ci exrates++; 35998c2ecf20Sopenharmony_ci } 36008c2ecf20Sopenharmony_ci 36018c2ecf20Sopenharmony_ci if (exrates > 8) 36028c2ecf20Sopenharmony_ci exrates -= 8; 36038c2ecf20Sopenharmony_ci else 36048c2ecf20Sopenharmony_ci exrates = 0; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci if (skb_tailroom(skb) < exrates + 2) 36078c2ecf20Sopenharmony_ci return -ENOMEM; 36088c2ecf20Sopenharmony_ci 36098c2ecf20Sopenharmony_ci if (exrates) { 36108c2ecf20Sopenharmony_ci pos = skb_put(skb, exrates + 2); 36118c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_EXT_SUPP_RATES; 36128c2ecf20Sopenharmony_ci *pos++ = exrates; 36138c2ecf20Sopenharmony_ci for (i = 8; i < sband->n_bitrates; i++) { 36148c2ecf20Sopenharmony_ci u8 basic = 0; 36158c2ecf20Sopenharmony_ci if ((rate_flags & sband->bitrates[i].flags) 36168c2ecf20Sopenharmony_ci != rate_flags) 36178c2ecf20Sopenharmony_ci continue; 36188c2ecf20Sopenharmony_ci if (need_basic && basic_rates & BIT(i)) 36198c2ecf20Sopenharmony_ci basic = 0x80; 36208c2ecf20Sopenharmony_ci rate = DIV_ROUND_UP(sband->bitrates[i].bitrate, 36218c2ecf20Sopenharmony_ci 5 * (1 << shift)); 36228c2ecf20Sopenharmony_ci *pos++ = basic | (u8) rate; 36238c2ecf20Sopenharmony_ci } 36248c2ecf20Sopenharmony_ci } 36258c2ecf20Sopenharmony_ci return 0; 36268c2ecf20Sopenharmony_ci} 36278c2ecf20Sopenharmony_ci 36288c2ecf20Sopenharmony_ciint ieee80211_ave_rssi(struct ieee80211_vif *vif) 36298c2ecf20Sopenharmony_ci{ 36308c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); 36318c2ecf20Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) { 36348c2ecf20Sopenharmony_ci /* non-managed type inferfaces */ 36358c2ecf20Sopenharmony_ci return 0; 36368c2ecf20Sopenharmony_ci } 36378c2ecf20Sopenharmony_ci return -ewma_beacon_signal_read(&ifmgd->ave_beacon_signal); 36388c2ecf20Sopenharmony_ci} 36398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_ave_rssi); 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_ciu8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) 36428c2ecf20Sopenharmony_ci{ 36438c2ecf20Sopenharmony_ci if (!mcs) 36448c2ecf20Sopenharmony_ci return 1; 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci /* TODO: consider rx_highest */ 36478c2ecf20Sopenharmony_ci 36488c2ecf20Sopenharmony_ci if (mcs->rx_mask[3]) 36498c2ecf20Sopenharmony_ci return 4; 36508c2ecf20Sopenharmony_ci if (mcs->rx_mask[2]) 36518c2ecf20Sopenharmony_ci return 3; 36528c2ecf20Sopenharmony_ci if (mcs->rx_mask[1]) 36538c2ecf20Sopenharmony_ci return 2; 36548c2ecf20Sopenharmony_ci return 1; 36558c2ecf20Sopenharmony_ci} 36568c2ecf20Sopenharmony_ci 36578c2ecf20Sopenharmony_ci/** 36588c2ecf20Sopenharmony_ci * ieee80211_calculate_rx_timestamp - calculate timestamp in frame 36598c2ecf20Sopenharmony_ci * @local: mac80211 hw info struct 36608c2ecf20Sopenharmony_ci * @status: RX status 36618c2ecf20Sopenharmony_ci * @mpdu_len: total MPDU length (including FCS) 36628c2ecf20Sopenharmony_ci * @mpdu_offset: offset into MPDU to calculate timestamp at 36638c2ecf20Sopenharmony_ci * 36648c2ecf20Sopenharmony_ci * This function calculates the RX timestamp at the given MPDU offset, taking 36658c2ecf20Sopenharmony_ci * into account what the RX timestamp was. An offset of 0 will just normalize 36668c2ecf20Sopenharmony_ci * the timestamp to TSF at beginning of MPDU reception. 36678c2ecf20Sopenharmony_ci */ 36688c2ecf20Sopenharmony_ciu64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, 36698c2ecf20Sopenharmony_ci struct ieee80211_rx_status *status, 36708c2ecf20Sopenharmony_ci unsigned int mpdu_len, 36718c2ecf20Sopenharmony_ci unsigned int mpdu_offset) 36728c2ecf20Sopenharmony_ci{ 36738c2ecf20Sopenharmony_ci u64 ts = status->mactime; 36748c2ecf20Sopenharmony_ci struct rate_info ri; 36758c2ecf20Sopenharmony_ci u16 rate; 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci if (WARN_ON(!ieee80211_have_rx_timestamp(status))) 36788c2ecf20Sopenharmony_ci return 0; 36798c2ecf20Sopenharmony_ci 36808c2ecf20Sopenharmony_ci memset(&ri, 0, sizeof(ri)); 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci ri.bw = status->bw; 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci /* Fill cfg80211 rate info */ 36858c2ecf20Sopenharmony_ci switch (status->encoding) { 36868c2ecf20Sopenharmony_ci case RX_ENC_HT: 36878c2ecf20Sopenharmony_ci ri.mcs = status->rate_idx; 36888c2ecf20Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_MCS; 36898c2ecf20Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 36908c2ecf20Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 36918c2ecf20Sopenharmony_ci break; 36928c2ecf20Sopenharmony_ci case RX_ENC_VHT: 36938c2ecf20Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_VHT_MCS; 36948c2ecf20Sopenharmony_ci ri.mcs = status->rate_idx; 36958c2ecf20Sopenharmony_ci ri.nss = status->nss; 36968c2ecf20Sopenharmony_ci if (status->enc_flags & RX_ENC_FLAG_SHORT_GI) 36978c2ecf20Sopenharmony_ci ri.flags |= RATE_INFO_FLAGS_SHORT_GI; 36988c2ecf20Sopenharmony_ci break; 36998c2ecf20Sopenharmony_ci default: 37008c2ecf20Sopenharmony_ci WARN_ON(1); 37018c2ecf20Sopenharmony_ci fallthrough; 37028c2ecf20Sopenharmony_ci case RX_ENC_LEGACY: { 37038c2ecf20Sopenharmony_ci struct ieee80211_supported_band *sband; 37048c2ecf20Sopenharmony_ci int shift = 0; 37058c2ecf20Sopenharmony_ci int bitrate; 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci switch (status->bw) { 37088c2ecf20Sopenharmony_ci case RATE_INFO_BW_10: 37098c2ecf20Sopenharmony_ci shift = 1; 37108c2ecf20Sopenharmony_ci break; 37118c2ecf20Sopenharmony_ci case RATE_INFO_BW_5: 37128c2ecf20Sopenharmony_ci shift = 2; 37138c2ecf20Sopenharmony_ci break; 37148c2ecf20Sopenharmony_ci } 37158c2ecf20Sopenharmony_ci 37168c2ecf20Sopenharmony_ci sband = local->hw.wiphy->bands[status->band]; 37178c2ecf20Sopenharmony_ci bitrate = sband->bitrates[status->rate_idx].bitrate; 37188c2ecf20Sopenharmony_ci ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift)); 37198c2ecf20Sopenharmony_ci 37208c2ecf20Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_PLCP_START) { 37218c2ecf20Sopenharmony_ci /* TODO: handle HT/VHT preambles */ 37228c2ecf20Sopenharmony_ci if (status->band == NL80211_BAND_5GHZ) { 37238c2ecf20Sopenharmony_ci ts += 20 << shift; 37248c2ecf20Sopenharmony_ci mpdu_offset += 2; 37258c2ecf20Sopenharmony_ci } else if (status->enc_flags & RX_ENC_FLAG_SHORTPRE) { 37268c2ecf20Sopenharmony_ci ts += 96; 37278c2ecf20Sopenharmony_ci } else { 37288c2ecf20Sopenharmony_ci ts += 192; 37298c2ecf20Sopenharmony_ci } 37308c2ecf20Sopenharmony_ci } 37318c2ecf20Sopenharmony_ci break; 37328c2ecf20Sopenharmony_ci } 37338c2ecf20Sopenharmony_ci } 37348c2ecf20Sopenharmony_ci 37358c2ecf20Sopenharmony_ci rate = cfg80211_calculate_bitrate(&ri); 37368c2ecf20Sopenharmony_ci if (WARN_ONCE(!rate, 37378c2ecf20Sopenharmony_ci "Invalid bitrate: flags=0x%llx, idx=%d, vht_nss=%d\n", 37388c2ecf20Sopenharmony_ci (unsigned long long)status->flag, status->rate_idx, 37398c2ecf20Sopenharmony_ci status->nss)) 37408c2ecf20Sopenharmony_ci return 0; 37418c2ecf20Sopenharmony_ci 37428c2ecf20Sopenharmony_ci /* rewind from end of MPDU */ 37438c2ecf20Sopenharmony_ci if (status->flag & RX_FLAG_MACTIME_END) 37448c2ecf20Sopenharmony_ci ts -= mpdu_len * 8 * 10 / rate; 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ci ts += mpdu_offset * 8 * 10 / rate; 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci return ts; 37498c2ecf20Sopenharmony_ci} 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_civoid ieee80211_dfs_cac_cancel(struct ieee80211_local *local) 37528c2ecf20Sopenharmony_ci{ 37538c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 37548c2ecf20Sopenharmony_ci struct cfg80211_chan_def chandef; 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */ 37578c2ecf20Sopenharmony_ci ASSERT_RTNL(); 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci mutex_lock(&local->mtx); 37608c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &local->interfaces, list) { 37618c2ecf20Sopenharmony_ci /* it might be waiting for the local->mtx, but then 37628c2ecf20Sopenharmony_ci * by the time it gets it, sdata->wdev.cac_started 37638c2ecf20Sopenharmony_ci * will no longer be true 37648c2ecf20Sopenharmony_ci */ 37658c2ecf20Sopenharmony_ci cancel_delayed_work(&sdata->dfs_cac_timer_work); 37668c2ecf20Sopenharmony_ci 37678c2ecf20Sopenharmony_ci if (sdata->wdev.cac_started) { 37688c2ecf20Sopenharmony_ci chandef = sdata->vif.bss_conf.chandef; 37698c2ecf20Sopenharmony_ci ieee80211_vif_release_channel(sdata); 37708c2ecf20Sopenharmony_ci cfg80211_cac_event(sdata->dev, 37718c2ecf20Sopenharmony_ci &chandef, 37728c2ecf20Sopenharmony_ci NL80211_RADAR_CAC_ABORTED, 37738c2ecf20Sopenharmony_ci GFP_KERNEL); 37748c2ecf20Sopenharmony_ci } 37758c2ecf20Sopenharmony_ci } 37768c2ecf20Sopenharmony_ci mutex_unlock(&local->mtx); 37778c2ecf20Sopenharmony_ci} 37788c2ecf20Sopenharmony_ci 37798c2ecf20Sopenharmony_civoid ieee80211_dfs_radar_detected_work(struct work_struct *work) 37808c2ecf20Sopenharmony_ci{ 37818c2ecf20Sopenharmony_ci struct ieee80211_local *local = 37828c2ecf20Sopenharmony_ci container_of(work, struct ieee80211_local, radar_detected_work); 37838c2ecf20Sopenharmony_ci struct cfg80211_chan_def chandef = local->hw.conf.chandef; 37848c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx; 37858c2ecf20Sopenharmony_ci int num_chanctx = 0; 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ci mutex_lock(&local->chanctx_mtx); 37888c2ecf20Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) { 37898c2ecf20Sopenharmony_ci if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) 37908c2ecf20Sopenharmony_ci continue; 37918c2ecf20Sopenharmony_ci 37928c2ecf20Sopenharmony_ci num_chanctx++; 37938c2ecf20Sopenharmony_ci chandef = ctx->conf.def; 37948c2ecf20Sopenharmony_ci } 37958c2ecf20Sopenharmony_ci mutex_unlock(&local->chanctx_mtx); 37968c2ecf20Sopenharmony_ci 37978c2ecf20Sopenharmony_ci rtnl_lock(); 37988c2ecf20Sopenharmony_ci ieee80211_dfs_cac_cancel(local); 37998c2ecf20Sopenharmony_ci rtnl_unlock(); 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci if (num_chanctx > 1) 38028c2ecf20Sopenharmony_ci /* XXX: multi-channel is not supported yet */ 38038c2ecf20Sopenharmony_ci WARN_ON(1); 38048c2ecf20Sopenharmony_ci else 38058c2ecf20Sopenharmony_ci cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); 38068c2ecf20Sopenharmony_ci} 38078c2ecf20Sopenharmony_ci 38088c2ecf20Sopenharmony_civoid ieee80211_radar_detected(struct ieee80211_hw *hw) 38098c2ecf20Sopenharmony_ci{ 38108c2ecf20Sopenharmony_ci struct ieee80211_local *local = hw_to_local(hw); 38118c2ecf20Sopenharmony_ci 38128c2ecf20Sopenharmony_ci trace_api_radar_detected(local); 38138c2ecf20Sopenharmony_ci 38148c2ecf20Sopenharmony_ci schedule_work(&local->radar_detected_work); 38158c2ecf20Sopenharmony_ci} 38168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_radar_detected); 38178c2ecf20Sopenharmony_ci 38188c2ecf20Sopenharmony_ciu32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) 38198c2ecf20Sopenharmony_ci{ 38208c2ecf20Sopenharmony_ci u32 ret; 38218c2ecf20Sopenharmony_ci int tmp; 38228c2ecf20Sopenharmony_ci 38238c2ecf20Sopenharmony_ci switch (c->width) { 38248c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 38258c2ecf20Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_20_NOHT; 38268c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 38278c2ecf20Sopenharmony_ci break; 38288c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 38298c2ecf20Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_20; 38308c2ecf20Sopenharmony_ci c->center_freq1 = c->chan->center_freq; 38318c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_40MHZ | 38328c2ecf20Sopenharmony_ci IEEE80211_STA_DISABLE_VHT; 38338c2ecf20Sopenharmony_ci break; 38348c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 38358c2ecf20Sopenharmony_ci tmp = (30 + c->chan->center_freq - c->center_freq1)/20; 38368c2ecf20Sopenharmony_ci /* n_P40 */ 38378c2ecf20Sopenharmony_ci tmp /= 2; 38388c2ecf20Sopenharmony_ci /* freq_P40 */ 38398c2ecf20Sopenharmony_ci c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; 38408c2ecf20Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_40; 38418c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_VHT; 38428c2ecf20Sopenharmony_ci break; 38438c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 38448c2ecf20Sopenharmony_ci c->center_freq2 = 0; 38458c2ecf20Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_80; 38468c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_80P80MHZ | 38478c2ecf20Sopenharmony_ci IEEE80211_STA_DISABLE_160MHZ; 38488c2ecf20Sopenharmony_ci break; 38498c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 38508c2ecf20Sopenharmony_ci /* n_P20 */ 38518c2ecf20Sopenharmony_ci tmp = (70 + c->chan->center_freq - c->center_freq1)/20; 38528c2ecf20Sopenharmony_ci /* n_P80 */ 38538c2ecf20Sopenharmony_ci tmp /= 4; 38548c2ecf20Sopenharmony_ci c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; 38558c2ecf20Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_80; 38568c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_80P80MHZ | 38578c2ecf20Sopenharmony_ci IEEE80211_STA_DISABLE_160MHZ; 38588c2ecf20Sopenharmony_ci break; 38598c2ecf20Sopenharmony_ci default: 38608c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 38618c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 38628c2ecf20Sopenharmony_ci c->width = NL80211_CHAN_WIDTH_20_NOHT; 38638c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 38648c2ecf20Sopenharmony_ci break; 38658c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_1: 38668c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_2: 38678c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_4: 38688c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_8: 38698c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_16: 38708c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 38718c2ecf20Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 38728c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 38738c2ecf20Sopenharmony_ci /* keep c->width */ 38748c2ecf20Sopenharmony_ci ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; 38758c2ecf20Sopenharmony_ci break; 38768c2ecf20Sopenharmony_ci } 38778c2ecf20Sopenharmony_ci 38788c2ecf20Sopenharmony_ci WARN_ON_ONCE(!cfg80211_chandef_valid(c)); 38798c2ecf20Sopenharmony_ci 38808c2ecf20Sopenharmony_ci return ret; 38818c2ecf20Sopenharmony_ci} 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci/* 38848c2ecf20Sopenharmony_ci * Returns true if smps_mode_new is strictly more restrictive than 38858c2ecf20Sopenharmony_ci * smps_mode_old. 38868c2ecf20Sopenharmony_ci */ 38878c2ecf20Sopenharmony_cibool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, 38888c2ecf20Sopenharmony_ci enum ieee80211_smps_mode smps_mode_new) 38898c2ecf20Sopenharmony_ci{ 38908c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || 38918c2ecf20Sopenharmony_ci smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) 38928c2ecf20Sopenharmony_ci return false; 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_ci switch (smps_mode_old) { 38958c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 38968c2ecf20Sopenharmony_ci return false; 38978c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 38988c2ecf20Sopenharmony_ci return smps_mode_new == IEEE80211_SMPS_STATIC; 38998c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 39008c2ecf20Sopenharmony_ci return smps_mode_new != IEEE80211_SMPS_OFF; 39018c2ecf20Sopenharmony_ci default: 39028c2ecf20Sopenharmony_ci WARN_ON(1); 39038c2ecf20Sopenharmony_ci } 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_ci return false; 39068c2ecf20Sopenharmony_ci} 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_ciint ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, 39098c2ecf20Sopenharmony_ci struct cfg80211_csa_settings *csa_settings) 39108c2ecf20Sopenharmony_ci{ 39118c2ecf20Sopenharmony_ci struct sk_buff *skb; 39128c2ecf20Sopenharmony_ci struct ieee80211_mgmt *mgmt; 39138c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 39148c2ecf20Sopenharmony_ci int freq; 39158c2ecf20Sopenharmony_ci int hdr_len = offsetofend(struct ieee80211_mgmt, 39168c2ecf20Sopenharmony_ci u.action.u.chan_switch); 39178c2ecf20Sopenharmony_ci u8 *pos; 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ci if (sdata->vif.type != NL80211_IFTYPE_ADHOC && 39208c2ecf20Sopenharmony_ci sdata->vif.type != NL80211_IFTYPE_MESH_POINT) 39218c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 39228c2ecf20Sopenharmony_ci 39238c2ecf20Sopenharmony_ci skb = dev_alloc_skb(local->tx_headroom + hdr_len + 39248c2ecf20Sopenharmony_ci 5 + /* channel switch announcement element */ 39258c2ecf20Sopenharmony_ci 3 + /* secondary channel offset element */ 39268c2ecf20Sopenharmony_ci 5 + /* wide bandwidth channel switch announcement */ 39278c2ecf20Sopenharmony_ci 8); /* mesh channel switch parameters element */ 39288c2ecf20Sopenharmony_ci if (!skb) 39298c2ecf20Sopenharmony_ci return -ENOMEM; 39308c2ecf20Sopenharmony_ci 39318c2ecf20Sopenharmony_ci skb_reserve(skb, local->tx_headroom); 39328c2ecf20Sopenharmony_ci mgmt = skb_put_zero(skb, hdr_len); 39338c2ecf20Sopenharmony_ci mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | 39348c2ecf20Sopenharmony_ci IEEE80211_STYPE_ACTION); 39358c2ecf20Sopenharmony_ci 39368c2ecf20Sopenharmony_ci eth_broadcast_addr(mgmt->da); 39378c2ecf20Sopenharmony_ci memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); 39388c2ecf20Sopenharmony_ci if (ieee80211_vif_is_mesh(&sdata->vif)) { 39398c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); 39408c2ecf20Sopenharmony_ci } else { 39418c2ecf20Sopenharmony_ci struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 39428c2ecf20Sopenharmony_ci memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); 39438c2ecf20Sopenharmony_ci } 39448c2ecf20Sopenharmony_ci mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; 39458c2ecf20Sopenharmony_ci mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; 39468c2ecf20Sopenharmony_ci pos = skb_put(skb, 5); 39478c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ 39488c2ecf20Sopenharmony_ci *pos++ = 3; /* IE length */ 39498c2ecf20Sopenharmony_ci *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ 39508c2ecf20Sopenharmony_ci freq = csa_settings->chandef.chan->center_freq; 39518c2ecf20Sopenharmony_ci *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ 39528c2ecf20Sopenharmony_ci *pos++ = csa_settings->count; /* count */ 39538c2ecf20Sopenharmony_ci 39548c2ecf20Sopenharmony_ci if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { 39558c2ecf20Sopenharmony_ci enum nl80211_channel_type ch_type; 39568c2ecf20Sopenharmony_ci 39578c2ecf20Sopenharmony_ci skb_put(skb, 3); 39588c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ 39598c2ecf20Sopenharmony_ci *pos++ = 1; /* IE length */ 39608c2ecf20Sopenharmony_ci ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); 39618c2ecf20Sopenharmony_ci if (ch_type == NL80211_CHAN_HT40PLUS) 39628c2ecf20Sopenharmony_ci *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; 39638c2ecf20Sopenharmony_ci else 39648c2ecf20Sopenharmony_ci *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; 39658c2ecf20Sopenharmony_ci } 39668c2ecf20Sopenharmony_ci 39678c2ecf20Sopenharmony_ci if (ieee80211_vif_is_mesh(&sdata->vif)) { 39688c2ecf20Sopenharmony_ci struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; 39698c2ecf20Sopenharmony_ci 39708c2ecf20Sopenharmony_ci skb_put(skb, 8); 39718c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ 39728c2ecf20Sopenharmony_ci *pos++ = 6; /* IE length */ 39738c2ecf20Sopenharmony_ci *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ 39748c2ecf20Sopenharmony_ci *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ 39758c2ecf20Sopenharmony_ci *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; 39768c2ecf20Sopenharmony_ci *pos++ |= csa_settings->block_tx ? 39778c2ecf20Sopenharmony_ci WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; 39788c2ecf20Sopenharmony_ci put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ 39798c2ecf20Sopenharmony_ci pos += 2; 39808c2ecf20Sopenharmony_ci put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */ 39818c2ecf20Sopenharmony_ci pos += 2; 39828c2ecf20Sopenharmony_ci } 39838c2ecf20Sopenharmony_ci 39848c2ecf20Sopenharmony_ci if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_80 || 39858c2ecf20Sopenharmony_ci csa_settings->chandef.width == NL80211_CHAN_WIDTH_80P80 || 39868c2ecf20Sopenharmony_ci csa_settings->chandef.width == NL80211_CHAN_WIDTH_160) { 39878c2ecf20Sopenharmony_ci skb_put(skb, 5); 39888c2ecf20Sopenharmony_ci ieee80211_ie_build_wide_bw_cs(pos, &csa_settings->chandef); 39898c2ecf20Sopenharmony_ci } 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_ci ieee80211_tx_skb(sdata, skb); 39928c2ecf20Sopenharmony_ci return 0; 39938c2ecf20Sopenharmony_ci} 39948c2ecf20Sopenharmony_ci 39958c2ecf20Sopenharmony_cibool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs) 39968c2ecf20Sopenharmony_ci{ 39978c2ecf20Sopenharmony_ci return !(cs == NULL || cs->cipher == 0 || 39988c2ecf20Sopenharmony_ci cs->hdr_len < cs->pn_len + cs->pn_off || 39998c2ecf20Sopenharmony_ci cs->hdr_len <= cs->key_idx_off || 40008c2ecf20Sopenharmony_ci cs->key_idx_shift > 7 || 40018c2ecf20Sopenharmony_ci cs->key_idx_mask == 0); 40028c2ecf20Sopenharmony_ci} 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_cibool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n) 40058c2ecf20Sopenharmony_ci{ 40068c2ecf20Sopenharmony_ci int i; 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci /* Ensure we have enough iftype bitmap space for all iftype values */ 40098c2ecf20Sopenharmony_ci WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype)); 40108c2ecf20Sopenharmony_ci 40118c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 40128c2ecf20Sopenharmony_ci if (!ieee80211_cs_valid(&cs[i])) 40138c2ecf20Sopenharmony_ci return false; 40148c2ecf20Sopenharmony_ci 40158c2ecf20Sopenharmony_ci return true; 40168c2ecf20Sopenharmony_ci} 40178c2ecf20Sopenharmony_ci 40188c2ecf20Sopenharmony_ciconst struct ieee80211_cipher_scheme * 40198c2ecf20Sopenharmony_ciieee80211_cs_get(struct ieee80211_local *local, u32 cipher, 40208c2ecf20Sopenharmony_ci enum nl80211_iftype iftype) 40218c2ecf20Sopenharmony_ci{ 40228c2ecf20Sopenharmony_ci const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes; 40238c2ecf20Sopenharmony_ci int n = local->hw.n_cipher_schemes; 40248c2ecf20Sopenharmony_ci int i; 40258c2ecf20Sopenharmony_ci const struct ieee80211_cipher_scheme *cs = NULL; 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 40288c2ecf20Sopenharmony_ci if (l[i].cipher == cipher) { 40298c2ecf20Sopenharmony_ci cs = &l[i]; 40308c2ecf20Sopenharmony_ci break; 40318c2ecf20Sopenharmony_ci } 40328c2ecf20Sopenharmony_ci } 40338c2ecf20Sopenharmony_ci 40348c2ecf20Sopenharmony_ci if (!cs || !(cs->iftype & BIT(iftype))) 40358c2ecf20Sopenharmony_ci return NULL; 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci return cs; 40388c2ecf20Sopenharmony_ci} 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_ciint ieee80211_cs_headroom(struct ieee80211_local *local, 40418c2ecf20Sopenharmony_ci struct cfg80211_crypto_settings *crypto, 40428c2ecf20Sopenharmony_ci enum nl80211_iftype iftype) 40438c2ecf20Sopenharmony_ci{ 40448c2ecf20Sopenharmony_ci const struct ieee80211_cipher_scheme *cs; 40458c2ecf20Sopenharmony_ci int headroom = IEEE80211_ENCRYPT_HEADROOM; 40468c2ecf20Sopenharmony_ci int i; 40478c2ecf20Sopenharmony_ci 40488c2ecf20Sopenharmony_ci for (i = 0; i < crypto->n_ciphers_pairwise; i++) { 40498c2ecf20Sopenharmony_ci cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i], 40508c2ecf20Sopenharmony_ci iftype); 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_ci if (cs && headroom < cs->hdr_len) 40538c2ecf20Sopenharmony_ci headroom = cs->hdr_len; 40548c2ecf20Sopenharmony_ci } 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci cs = ieee80211_cs_get(local, crypto->cipher_group, iftype); 40578c2ecf20Sopenharmony_ci if (cs && headroom < cs->hdr_len) 40588c2ecf20Sopenharmony_ci headroom = cs->hdr_len; 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_ci return headroom; 40618c2ecf20Sopenharmony_ci} 40628c2ecf20Sopenharmony_ci 40638c2ecf20Sopenharmony_cistatic bool 40648c2ecf20Sopenharmony_ciieee80211_extend_noa_desc(struct ieee80211_noa_data *data, u32 tsf, int i) 40658c2ecf20Sopenharmony_ci{ 40668c2ecf20Sopenharmony_ci s32 end = data->desc[i].start + data->desc[i].duration - (tsf + 1); 40678c2ecf20Sopenharmony_ci int skip; 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci if (end > 0) 40708c2ecf20Sopenharmony_ci return false; 40718c2ecf20Sopenharmony_ci 40728c2ecf20Sopenharmony_ci /* One shot NOA */ 40738c2ecf20Sopenharmony_ci if (data->count[i] == 1) 40748c2ecf20Sopenharmony_ci return false; 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci if (data->desc[i].interval == 0) 40778c2ecf20Sopenharmony_ci return false; 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_ci /* End time is in the past, check for repetitions */ 40808c2ecf20Sopenharmony_ci skip = DIV_ROUND_UP(-end, data->desc[i].interval); 40818c2ecf20Sopenharmony_ci if (data->count[i] < 255) { 40828c2ecf20Sopenharmony_ci if (data->count[i] <= skip) { 40838c2ecf20Sopenharmony_ci data->count[i] = 0; 40848c2ecf20Sopenharmony_ci return false; 40858c2ecf20Sopenharmony_ci } 40868c2ecf20Sopenharmony_ci 40878c2ecf20Sopenharmony_ci data->count[i] -= skip; 40888c2ecf20Sopenharmony_ci } 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci data->desc[i].start += skip * data->desc[i].interval; 40918c2ecf20Sopenharmony_ci 40928c2ecf20Sopenharmony_ci return true; 40938c2ecf20Sopenharmony_ci} 40948c2ecf20Sopenharmony_ci 40958c2ecf20Sopenharmony_cistatic bool 40968c2ecf20Sopenharmony_ciieee80211_extend_absent_time(struct ieee80211_noa_data *data, u32 tsf, 40978c2ecf20Sopenharmony_ci s32 *offset) 40988c2ecf20Sopenharmony_ci{ 40998c2ecf20Sopenharmony_ci bool ret = false; 41008c2ecf20Sopenharmony_ci int i; 41018c2ecf20Sopenharmony_ci 41028c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 41038c2ecf20Sopenharmony_ci s32 cur; 41048c2ecf20Sopenharmony_ci 41058c2ecf20Sopenharmony_ci if (!data->count[i]) 41068c2ecf20Sopenharmony_ci continue; 41078c2ecf20Sopenharmony_ci 41088c2ecf20Sopenharmony_ci if (ieee80211_extend_noa_desc(data, tsf + *offset, i)) 41098c2ecf20Sopenharmony_ci ret = true; 41108c2ecf20Sopenharmony_ci 41118c2ecf20Sopenharmony_ci cur = data->desc[i].start - tsf; 41128c2ecf20Sopenharmony_ci if (cur > *offset) 41138c2ecf20Sopenharmony_ci continue; 41148c2ecf20Sopenharmony_ci 41158c2ecf20Sopenharmony_ci cur = data->desc[i].start + data->desc[i].duration - tsf; 41168c2ecf20Sopenharmony_ci if (cur > *offset) 41178c2ecf20Sopenharmony_ci *offset = cur; 41188c2ecf20Sopenharmony_ci } 41198c2ecf20Sopenharmony_ci 41208c2ecf20Sopenharmony_ci return ret; 41218c2ecf20Sopenharmony_ci} 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_cistatic u32 41248c2ecf20Sopenharmony_ciieee80211_get_noa_absent_time(struct ieee80211_noa_data *data, u32 tsf) 41258c2ecf20Sopenharmony_ci{ 41268c2ecf20Sopenharmony_ci s32 offset = 0; 41278c2ecf20Sopenharmony_ci int tries = 0; 41288c2ecf20Sopenharmony_ci /* 41298c2ecf20Sopenharmony_ci * arbitrary limit, used to avoid infinite loops when combined NoA 41308c2ecf20Sopenharmony_ci * descriptors cover the full time period. 41318c2ecf20Sopenharmony_ci */ 41328c2ecf20Sopenharmony_ci int max_tries = 5; 41338c2ecf20Sopenharmony_ci 41348c2ecf20Sopenharmony_ci ieee80211_extend_absent_time(data, tsf, &offset); 41358c2ecf20Sopenharmony_ci do { 41368c2ecf20Sopenharmony_ci if (!ieee80211_extend_absent_time(data, tsf, &offset)) 41378c2ecf20Sopenharmony_ci break; 41388c2ecf20Sopenharmony_ci 41398c2ecf20Sopenharmony_ci tries++; 41408c2ecf20Sopenharmony_ci } while (tries < max_tries); 41418c2ecf20Sopenharmony_ci 41428c2ecf20Sopenharmony_ci return offset; 41438c2ecf20Sopenharmony_ci} 41448c2ecf20Sopenharmony_ci 41458c2ecf20Sopenharmony_civoid ieee80211_update_p2p_noa(struct ieee80211_noa_data *data, u32 tsf) 41468c2ecf20Sopenharmony_ci{ 41478c2ecf20Sopenharmony_ci u32 next_offset = BIT(31) - 1; 41488c2ecf20Sopenharmony_ci int i; 41498c2ecf20Sopenharmony_ci 41508c2ecf20Sopenharmony_ci data->absent = 0; 41518c2ecf20Sopenharmony_ci data->has_next_tsf = false; 41528c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 41538c2ecf20Sopenharmony_ci s32 start; 41548c2ecf20Sopenharmony_ci 41558c2ecf20Sopenharmony_ci if (!data->count[i]) 41568c2ecf20Sopenharmony_ci continue; 41578c2ecf20Sopenharmony_ci 41588c2ecf20Sopenharmony_ci ieee80211_extend_noa_desc(data, tsf, i); 41598c2ecf20Sopenharmony_ci start = data->desc[i].start - tsf; 41608c2ecf20Sopenharmony_ci if (start <= 0) 41618c2ecf20Sopenharmony_ci data->absent |= BIT(i); 41628c2ecf20Sopenharmony_ci 41638c2ecf20Sopenharmony_ci if (next_offset > start) 41648c2ecf20Sopenharmony_ci next_offset = start; 41658c2ecf20Sopenharmony_ci 41668c2ecf20Sopenharmony_ci data->has_next_tsf = true; 41678c2ecf20Sopenharmony_ci } 41688c2ecf20Sopenharmony_ci 41698c2ecf20Sopenharmony_ci if (data->absent) 41708c2ecf20Sopenharmony_ci next_offset = ieee80211_get_noa_absent_time(data, tsf); 41718c2ecf20Sopenharmony_ci 41728c2ecf20Sopenharmony_ci data->next_tsf = tsf + next_offset; 41738c2ecf20Sopenharmony_ci} 41748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_update_p2p_noa); 41758c2ecf20Sopenharmony_ci 41768c2ecf20Sopenharmony_ciint ieee80211_parse_p2p_noa(const struct ieee80211_p2p_noa_attr *attr, 41778c2ecf20Sopenharmony_ci struct ieee80211_noa_data *data, u32 tsf) 41788c2ecf20Sopenharmony_ci{ 41798c2ecf20Sopenharmony_ci int ret = 0; 41808c2ecf20Sopenharmony_ci int i; 41818c2ecf20Sopenharmony_ci 41828c2ecf20Sopenharmony_ci memset(data, 0, sizeof(*data)); 41838c2ecf20Sopenharmony_ci 41848c2ecf20Sopenharmony_ci for (i = 0; i < IEEE80211_P2P_NOA_DESC_MAX; i++) { 41858c2ecf20Sopenharmony_ci const struct ieee80211_p2p_noa_desc *desc = &attr->desc[i]; 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci if (!desc->count || !desc->duration) 41888c2ecf20Sopenharmony_ci continue; 41898c2ecf20Sopenharmony_ci 41908c2ecf20Sopenharmony_ci data->count[i] = desc->count; 41918c2ecf20Sopenharmony_ci data->desc[i].start = le32_to_cpu(desc->start_time); 41928c2ecf20Sopenharmony_ci data->desc[i].duration = le32_to_cpu(desc->duration); 41938c2ecf20Sopenharmony_ci data->desc[i].interval = le32_to_cpu(desc->interval); 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_ci if (data->count[i] > 1 && 41968c2ecf20Sopenharmony_ci data->desc[i].interval < data->desc[i].duration) 41978c2ecf20Sopenharmony_ci continue; 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci ieee80211_extend_noa_desc(data, tsf, i); 42008c2ecf20Sopenharmony_ci ret++; 42018c2ecf20Sopenharmony_ci } 42028c2ecf20Sopenharmony_ci 42038c2ecf20Sopenharmony_ci if (ret) 42048c2ecf20Sopenharmony_ci ieee80211_update_p2p_noa(data, tsf); 42058c2ecf20Sopenharmony_ci 42068c2ecf20Sopenharmony_ci return ret; 42078c2ecf20Sopenharmony_ci} 42088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_parse_p2p_noa); 42098c2ecf20Sopenharmony_ci 42108c2ecf20Sopenharmony_civoid ieee80211_recalc_dtim(struct ieee80211_local *local, 42118c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata) 42128c2ecf20Sopenharmony_ci{ 42138c2ecf20Sopenharmony_ci u64 tsf = drv_get_tsf(local, sdata); 42148c2ecf20Sopenharmony_ci u64 dtim_count = 0; 42158c2ecf20Sopenharmony_ci u16 beacon_int = sdata->vif.bss_conf.beacon_int * 1024; 42168c2ecf20Sopenharmony_ci u8 dtim_period = sdata->vif.bss_conf.dtim_period; 42178c2ecf20Sopenharmony_ci struct ps_data *ps; 42188c2ecf20Sopenharmony_ci u8 bcns_from_dtim; 42198c2ecf20Sopenharmony_ci 42208c2ecf20Sopenharmony_ci if (tsf == -1ULL || !beacon_int || !dtim_period) 42218c2ecf20Sopenharmony_ci return; 42228c2ecf20Sopenharmony_ci 42238c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP || 42248c2ecf20Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { 42258c2ecf20Sopenharmony_ci if (!sdata->bss) 42268c2ecf20Sopenharmony_ci return; 42278c2ecf20Sopenharmony_ci 42288c2ecf20Sopenharmony_ci ps = &sdata->bss->ps; 42298c2ecf20Sopenharmony_ci } else if (ieee80211_vif_is_mesh(&sdata->vif)) { 42308c2ecf20Sopenharmony_ci ps = &sdata->u.mesh.ps; 42318c2ecf20Sopenharmony_ci } else { 42328c2ecf20Sopenharmony_ci return; 42338c2ecf20Sopenharmony_ci } 42348c2ecf20Sopenharmony_ci 42358c2ecf20Sopenharmony_ci /* 42368c2ecf20Sopenharmony_ci * actually finds last dtim_count, mac80211 will update in 42378c2ecf20Sopenharmony_ci * __beacon_add_tim(). 42388c2ecf20Sopenharmony_ci * dtim_count = dtim_period - (tsf / bcn_int) % dtim_period 42398c2ecf20Sopenharmony_ci */ 42408c2ecf20Sopenharmony_ci do_div(tsf, beacon_int); 42418c2ecf20Sopenharmony_ci bcns_from_dtim = do_div(tsf, dtim_period); 42428c2ecf20Sopenharmony_ci /* just had a DTIM */ 42438c2ecf20Sopenharmony_ci if (!bcns_from_dtim) 42448c2ecf20Sopenharmony_ci dtim_count = 0; 42458c2ecf20Sopenharmony_ci else 42468c2ecf20Sopenharmony_ci dtim_count = dtim_period - bcns_from_dtim; 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_ci ps->dtim_count = dtim_count; 42498c2ecf20Sopenharmony_ci} 42508c2ecf20Sopenharmony_ci 42518c2ecf20Sopenharmony_cistatic u8 ieee80211_chanctx_radar_detect(struct ieee80211_local *local, 42528c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx) 42538c2ecf20Sopenharmony_ci{ 42548c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 42558c2ecf20Sopenharmony_ci u8 radar_detect = 0; 42568c2ecf20Sopenharmony_ci 42578c2ecf20Sopenharmony_ci lockdep_assert_held(&local->chanctx_mtx); 42588c2ecf20Sopenharmony_ci 42598c2ecf20Sopenharmony_ci if (WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED)) 42608c2ecf20Sopenharmony_ci return 0; 42618c2ecf20Sopenharmony_ci 42628c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) 42638c2ecf20Sopenharmony_ci if (sdata->reserved_radar_required) 42648c2ecf20Sopenharmony_ci radar_detect |= BIT(sdata->reserved_chandef.width); 42658c2ecf20Sopenharmony_ci 42668c2ecf20Sopenharmony_ci /* 42678c2ecf20Sopenharmony_ci * An in-place reservation context should not have any assigned vifs 42688c2ecf20Sopenharmony_ci * until it replaces the other context. 42698c2ecf20Sopenharmony_ci */ 42708c2ecf20Sopenharmony_ci WARN_ON(ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER && 42718c2ecf20Sopenharmony_ci !list_empty(&ctx->assigned_vifs)); 42728c2ecf20Sopenharmony_ci 42738c2ecf20Sopenharmony_ci list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) 42748c2ecf20Sopenharmony_ci if (sdata->radar_required) 42758c2ecf20Sopenharmony_ci radar_detect |= BIT(sdata->vif.bss_conf.chandef.width); 42768c2ecf20Sopenharmony_ci 42778c2ecf20Sopenharmony_ci return radar_detect; 42788c2ecf20Sopenharmony_ci} 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ciint ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, 42818c2ecf20Sopenharmony_ci const struct cfg80211_chan_def *chandef, 42828c2ecf20Sopenharmony_ci enum ieee80211_chanctx_mode chanmode, 42838c2ecf20Sopenharmony_ci u8 radar_detect) 42848c2ecf20Sopenharmony_ci{ 42858c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 42868c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata_iter; 42878c2ecf20Sopenharmony_ci enum nl80211_iftype iftype = sdata->wdev.iftype; 42888c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx; 42898c2ecf20Sopenharmony_ci int total = 1; 42908c2ecf20Sopenharmony_ci struct iface_combination_params params = { 42918c2ecf20Sopenharmony_ci .radar_detect = radar_detect, 42928c2ecf20Sopenharmony_ci }; 42938c2ecf20Sopenharmony_ci 42948c2ecf20Sopenharmony_ci lockdep_assert_held(&local->chanctx_mtx); 42958c2ecf20Sopenharmony_ci 42968c2ecf20Sopenharmony_ci if (WARN_ON(hweight32(radar_detect) > 1)) 42978c2ecf20Sopenharmony_ci return -EINVAL; 42988c2ecf20Sopenharmony_ci 42998c2ecf20Sopenharmony_ci if (WARN_ON(chandef && chanmode == IEEE80211_CHANCTX_SHARED && 43008c2ecf20Sopenharmony_ci !chandef->chan)) 43018c2ecf20Sopenharmony_ci return -EINVAL; 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci if (WARN_ON(iftype >= NUM_NL80211_IFTYPES)) 43048c2ecf20Sopenharmony_ci return -EINVAL; 43058c2ecf20Sopenharmony_ci 43068c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_AP || 43078c2ecf20Sopenharmony_ci sdata->vif.type == NL80211_IFTYPE_MESH_POINT) { 43088c2ecf20Sopenharmony_ci /* 43098c2ecf20Sopenharmony_ci * always passing this is harmless, since it'll be the 43108c2ecf20Sopenharmony_ci * same value that cfg80211 finds if it finds the same 43118c2ecf20Sopenharmony_ci * interface ... and that's always allowed 43128c2ecf20Sopenharmony_ci */ 43138c2ecf20Sopenharmony_ci params.new_beacon_int = sdata->vif.bss_conf.beacon_int; 43148c2ecf20Sopenharmony_ci } 43158c2ecf20Sopenharmony_ci 43168c2ecf20Sopenharmony_ci /* Always allow software iftypes */ 43178c2ecf20Sopenharmony_ci if (cfg80211_iftype_allowed(local->hw.wiphy, iftype, 0, 1)) { 43188c2ecf20Sopenharmony_ci if (radar_detect) 43198c2ecf20Sopenharmony_ci return -EINVAL; 43208c2ecf20Sopenharmony_ci return 0; 43218c2ecf20Sopenharmony_ci } 43228c2ecf20Sopenharmony_ci 43238c2ecf20Sopenharmony_ci if (chandef) 43248c2ecf20Sopenharmony_ci params.num_different_channels = 1; 43258c2ecf20Sopenharmony_ci 43268c2ecf20Sopenharmony_ci if (iftype != NL80211_IFTYPE_UNSPECIFIED) 43278c2ecf20Sopenharmony_ci params.iftype_num[iftype] = 1; 43288c2ecf20Sopenharmony_ci 43298c2ecf20Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) { 43308c2ecf20Sopenharmony_ci if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 43318c2ecf20Sopenharmony_ci continue; 43328c2ecf20Sopenharmony_ci params.radar_detect |= 43338c2ecf20Sopenharmony_ci ieee80211_chanctx_radar_detect(local, ctx); 43348c2ecf20Sopenharmony_ci if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) { 43358c2ecf20Sopenharmony_ci params.num_different_channels++; 43368c2ecf20Sopenharmony_ci continue; 43378c2ecf20Sopenharmony_ci } 43388c2ecf20Sopenharmony_ci if (chandef && chanmode == IEEE80211_CHANCTX_SHARED && 43398c2ecf20Sopenharmony_ci cfg80211_chandef_compatible(chandef, 43408c2ecf20Sopenharmony_ci &ctx->conf.def)) 43418c2ecf20Sopenharmony_ci continue; 43428c2ecf20Sopenharmony_ci params.num_different_channels++; 43438c2ecf20Sopenharmony_ci } 43448c2ecf20Sopenharmony_ci 43458c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdata_iter, &local->interfaces, list) { 43468c2ecf20Sopenharmony_ci struct wireless_dev *wdev_iter; 43478c2ecf20Sopenharmony_ci 43488c2ecf20Sopenharmony_ci wdev_iter = &sdata_iter->wdev; 43498c2ecf20Sopenharmony_ci 43508c2ecf20Sopenharmony_ci if (sdata_iter == sdata || 43518c2ecf20Sopenharmony_ci !ieee80211_sdata_running(sdata_iter) || 43528c2ecf20Sopenharmony_ci cfg80211_iftype_allowed(local->hw.wiphy, 43538c2ecf20Sopenharmony_ci wdev_iter->iftype, 0, 1)) 43548c2ecf20Sopenharmony_ci continue; 43558c2ecf20Sopenharmony_ci 43568c2ecf20Sopenharmony_ci params.iftype_num[wdev_iter->iftype]++; 43578c2ecf20Sopenharmony_ci total++; 43588c2ecf20Sopenharmony_ci } 43598c2ecf20Sopenharmony_ci 43608c2ecf20Sopenharmony_ci if (total == 1 && !params.radar_detect) 43618c2ecf20Sopenharmony_ci return 0; 43628c2ecf20Sopenharmony_ci 43638c2ecf20Sopenharmony_ci return cfg80211_check_combinations(local->hw.wiphy, ¶ms); 43648c2ecf20Sopenharmony_ci} 43658c2ecf20Sopenharmony_ci 43668c2ecf20Sopenharmony_cistatic void 43678c2ecf20Sopenharmony_ciieee80211_iter_max_chans(const struct ieee80211_iface_combination *c, 43688c2ecf20Sopenharmony_ci void *data) 43698c2ecf20Sopenharmony_ci{ 43708c2ecf20Sopenharmony_ci u32 *max_num_different_channels = data; 43718c2ecf20Sopenharmony_ci 43728c2ecf20Sopenharmony_ci *max_num_different_channels = max(*max_num_different_channels, 43738c2ecf20Sopenharmony_ci c->num_different_channels); 43748c2ecf20Sopenharmony_ci} 43758c2ecf20Sopenharmony_ci 43768c2ecf20Sopenharmony_ciint ieee80211_max_num_channels(struct ieee80211_local *local) 43778c2ecf20Sopenharmony_ci{ 43788c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata; 43798c2ecf20Sopenharmony_ci struct ieee80211_chanctx *ctx; 43808c2ecf20Sopenharmony_ci u32 max_num_different_channels = 1; 43818c2ecf20Sopenharmony_ci int err; 43828c2ecf20Sopenharmony_ci struct iface_combination_params params = {0}; 43838c2ecf20Sopenharmony_ci 43848c2ecf20Sopenharmony_ci lockdep_assert_held(&local->chanctx_mtx); 43858c2ecf20Sopenharmony_ci 43868c2ecf20Sopenharmony_ci list_for_each_entry(ctx, &local->chanctx_list, list) { 43878c2ecf20Sopenharmony_ci if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) 43888c2ecf20Sopenharmony_ci continue; 43898c2ecf20Sopenharmony_ci 43908c2ecf20Sopenharmony_ci params.num_different_channels++; 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci params.radar_detect |= 43938c2ecf20Sopenharmony_ci ieee80211_chanctx_radar_detect(local, ctx); 43948c2ecf20Sopenharmony_ci } 43958c2ecf20Sopenharmony_ci 43968c2ecf20Sopenharmony_ci list_for_each_entry_rcu(sdata, &local->interfaces, list) 43978c2ecf20Sopenharmony_ci params.iftype_num[sdata->wdev.iftype]++; 43988c2ecf20Sopenharmony_ci 43998c2ecf20Sopenharmony_ci err = cfg80211_iter_combinations(local->hw.wiphy, ¶ms, 44008c2ecf20Sopenharmony_ci ieee80211_iter_max_chans, 44018c2ecf20Sopenharmony_ci &max_num_different_channels); 44028c2ecf20Sopenharmony_ci if (err < 0) 44038c2ecf20Sopenharmony_ci return err; 44048c2ecf20Sopenharmony_ci 44058c2ecf20Sopenharmony_ci return max_num_different_channels; 44068c2ecf20Sopenharmony_ci} 44078c2ecf20Sopenharmony_ci 44088c2ecf20Sopenharmony_civoid ieee80211_add_s1g_capab_ie(struct ieee80211_sub_if_data *sdata, 44098c2ecf20Sopenharmony_ci struct ieee80211_sta_s1g_cap *caps, 44108c2ecf20Sopenharmony_ci struct sk_buff *skb) 44118c2ecf20Sopenharmony_ci{ 44128c2ecf20Sopenharmony_ci struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 44138c2ecf20Sopenharmony_ci struct ieee80211_s1g_cap s1g_capab; 44148c2ecf20Sopenharmony_ci u8 *pos; 44158c2ecf20Sopenharmony_ci int i; 44168c2ecf20Sopenharmony_ci 44178c2ecf20Sopenharmony_ci if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) 44188c2ecf20Sopenharmony_ci return; 44198c2ecf20Sopenharmony_ci 44208c2ecf20Sopenharmony_ci if (!caps->s1g) 44218c2ecf20Sopenharmony_ci return; 44228c2ecf20Sopenharmony_ci 44238c2ecf20Sopenharmony_ci memcpy(s1g_capab.capab_info, caps->cap, sizeof(caps->cap)); 44248c2ecf20Sopenharmony_ci memcpy(s1g_capab.supp_mcs_nss, caps->nss_mcs, sizeof(caps->nss_mcs)); 44258c2ecf20Sopenharmony_ci 44268c2ecf20Sopenharmony_ci /* override the capability info */ 44278c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(ifmgd->s1g_capa.capab_info); i++) { 44288c2ecf20Sopenharmony_ci u8 mask = ifmgd->s1g_capa_mask.capab_info[i]; 44298c2ecf20Sopenharmony_ci 44308c2ecf20Sopenharmony_ci s1g_capab.capab_info[i] &= ~mask; 44318c2ecf20Sopenharmony_ci s1g_capab.capab_info[i] |= ifmgd->s1g_capa.capab_info[i] & mask; 44328c2ecf20Sopenharmony_ci } 44338c2ecf20Sopenharmony_ci 44348c2ecf20Sopenharmony_ci /* then MCS and NSS set */ 44358c2ecf20Sopenharmony_ci for (i = 0; i < sizeof(ifmgd->s1g_capa.supp_mcs_nss); i++) { 44368c2ecf20Sopenharmony_ci u8 mask = ifmgd->s1g_capa_mask.supp_mcs_nss[i]; 44378c2ecf20Sopenharmony_ci 44388c2ecf20Sopenharmony_ci s1g_capab.supp_mcs_nss[i] &= ~mask; 44398c2ecf20Sopenharmony_ci s1g_capab.supp_mcs_nss[i] |= 44408c2ecf20Sopenharmony_ci ifmgd->s1g_capa.supp_mcs_nss[i] & mask; 44418c2ecf20Sopenharmony_ci } 44428c2ecf20Sopenharmony_ci 44438c2ecf20Sopenharmony_ci pos = skb_put(skb, 2 + sizeof(s1g_capab)); 44448c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_S1G_CAPABILITIES; 44458c2ecf20Sopenharmony_ci *pos++ = sizeof(s1g_capab); 44468c2ecf20Sopenharmony_ci 44478c2ecf20Sopenharmony_ci memcpy(pos, &s1g_capab, sizeof(s1g_capab)); 44488c2ecf20Sopenharmony_ci} 44498c2ecf20Sopenharmony_ci 44508c2ecf20Sopenharmony_civoid ieee80211_add_aid_request_ie(struct ieee80211_sub_if_data *sdata, 44518c2ecf20Sopenharmony_ci struct sk_buff *skb) 44528c2ecf20Sopenharmony_ci{ 44538c2ecf20Sopenharmony_ci u8 *pos = skb_put(skb, 3); 44548c2ecf20Sopenharmony_ci 44558c2ecf20Sopenharmony_ci *pos++ = WLAN_EID_AID_REQUEST; 44568c2ecf20Sopenharmony_ci *pos++ = 1; 44578c2ecf20Sopenharmony_ci *pos++ = 0; 44588c2ecf20Sopenharmony_ci} 44598c2ecf20Sopenharmony_ci 44608c2ecf20Sopenharmony_ciu8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) 44618c2ecf20Sopenharmony_ci{ 44628c2ecf20Sopenharmony_ci *buf++ = WLAN_EID_VENDOR_SPECIFIC; 44638c2ecf20Sopenharmony_ci *buf++ = 7; /* len */ 44648c2ecf20Sopenharmony_ci *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ 44658c2ecf20Sopenharmony_ci *buf++ = 0x50; 44668c2ecf20Sopenharmony_ci *buf++ = 0xf2; 44678c2ecf20Sopenharmony_ci *buf++ = 2; /* WME */ 44688c2ecf20Sopenharmony_ci *buf++ = 0; /* WME info */ 44698c2ecf20Sopenharmony_ci *buf++ = 1; /* WME ver */ 44708c2ecf20Sopenharmony_ci *buf++ = qosinfo; /* U-APSD no in use */ 44718c2ecf20Sopenharmony_ci 44728c2ecf20Sopenharmony_ci return buf; 44738c2ecf20Sopenharmony_ci} 44748c2ecf20Sopenharmony_ci 44758c2ecf20Sopenharmony_civoid ieee80211_txq_get_depth(struct ieee80211_txq *txq, 44768c2ecf20Sopenharmony_ci unsigned long *frame_cnt, 44778c2ecf20Sopenharmony_ci unsigned long *byte_cnt) 44788c2ecf20Sopenharmony_ci{ 44798c2ecf20Sopenharmony_ci struct txq_info *txqi = to_txq_info(txq); 44808c2ecf20Sopenharmony_ci u32 frag_cnt = 0, frag_bytes = 0; 44818c2ecf20Sopenharmony_ci struct sk_buff *skb; 44828c2ecf20Sopenharmony_ci 44838c2ecf20Sopenharmony_ci skb_queue_walk(&txqi->frags, skb) { 44848c2ecf20Sopenharmony_ci frag_cnt++; 44858c2ecf20Sopenharmony_ci frag_bytes += skb->len; 44868c2ecf20Sopenharmony_ci } 44878c2ecf20Sopenharmony_ci 44888c2ecf20Sopenharmony_ci if (frame_cnt) 44898c2ecf20Sopenharmony_ci *frame_cnt = txqi->tin.backlog_packets + frag_cnt; 44908c2ecf20Sopenharmony_ci 44918c2ecf20Sopenharmony_ci if (byte_cnt) 44928c2ecf20Sopenharmony_ci *byte_cnt = txqi->tin.backlog_bytes + frag_bytes; 44938c2ecf20Sopenharmony_ci} 44948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ieee80211_txq_get_depth); 44958c2ecf20Sopenharmony_ci 44968c2ecf20Sopenharmony_ciconst u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = { 44978c2ecf20Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, 44988c2ecf20Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_VI, 44998c2ecf20Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_BE, 45008c2ecf20Sopenharmony_ci IEEE80211_WMM_IE_STA_QOSINFO_AC_BK 45018c2ecf20Sopenharmony_ci}; 45028c2ecf20Sopenharmony_ci 45038c2ecf20Sopenharmony_ciu16 ieee80211_encode_usf(int listen_interval) 45048c2ecf20Sopenharmony_ci{ 45058c2ecf20Sopenharmony_ci static const int listen_int_usf[] = { 1, 10, 1000, 10000 }; 45068c2ecf20Sopenharmony_ci u16 ui, usf = 0; 45078c2ecf20Sopenharmony_ci 45088c2ecf20Sopenharmony_ci /* find greatest USF */ 45098c2ecf20Sopenharmony_ci while (usf < IEEE80211_MAX_USF) { 45108c2ecf20Sopenharmony_ci if (listen_interval % listen_int_usf[usf + 1]) 45118c2ecf20Sopenharmony_ci break; 45128c2ecf20Sopenharmony_ci usf += 1; 45138c2ecf20Sopenharmony_ci } 45148c2ecf20Sopenharmony_ci ui = listen_interval / listen_int_usf[usf]; 45158c2ecf20Sopenharmony_ci 45168c2ecf20Sopenharmony_ci /* error if there is a remainder. Should've been checked by user */ 45178c2ecf20Sopenharmony_ci WARN_ON_ONCE(ui > IEEE80211_MAX_UI); 45188c2ecf20Sopenharmony_ci listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) | 45198c2ecf20Sopenharmony_ci FIELD_PREP(LISTEN_INT_UI, ui); 45208c2ecf20Sopenharmony_ci 45218c2ecf20Sopenharmony_ci return (u16) listen_interval; 45228c2ecf20Sopenharmony_ci} 4523