Commit 41cbb0f5 authored by Luca Coelho's avatar Luca Coelho Committed by Johannes Berg

mac80211: add support for HE

Add support for HE in mac80211 conforming with P802.11ax_D1.4.

Johannes: Fix another bug with the buf_size comparison in agg-rx.c.
Signed-off-by: default avatarLiad Kaufman <liad.kaufman@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarIlan Peer <ilan.peer@intel.com>
Signed-off-by: default avatarIdo Yariv <idox.yariv@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent b8042b3d
......@@ -23,6 +23,7 @@
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <net/codel.h>
#include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h>
/**
......@@ -162,6 +163,8 @@ enum ieee80211_ac_numbers {
* @txop: maximum burst time in units of 32 usecs, 0 meaning disabled
* @acm: is mandatory admission control required for the access category
* @uapsd: is U-APSD mode enabled for the queue
* @mu_edca: is the MU EDCA configured
* @mu_edca_param_rec: MU EDCA Parameter Record for HE
*/
struct ieee80211_tx_queue_params {
u16 txop;
......@@ -170,6 +173,8 @@ struct ieee80211_tx_queue_params {
u8 aifs;
bool acm;
bool uapsd;
bool mu_edca;
struct ieee80211_he_mu_edca_param_ac_rec mu_edca_param_rec;
};
struct ieee80211_low_level_stats {
......@@ -463,6 +468,15 @@ struct ieee80211_mu_group_data {
* This structure keeps information about a BSS (and an association
* to that BSS) that can change during the lifetime of the BSS.
*
* @bss_color: 6-bit value to mark inter-BSS frame, if BSS supports HE
* @htc_trig_based_pkt_ext: default PE in 4us units, if BSS supports HE
* @multi_sta_back_32bit: supports BA bitmap of 32-bits in Multi-STA BACK
* @uora_exists: is the UORA element advertised by AP
* @ack_enabled: indicates support to receive a multi-TID that solicits either
* ACK, BACK or both
* @uora_ocw_range: UORA element's OCW Range field
* @frame_time_rts_th: HE duration RTS threshold, in units of 32us
* @he_support: does this BSS support HE
* @assoc: association status
* @ibss_joined: indicates whether this station is part of an IBSS
* or not
......@@ -550,6 +564,14 @@ struct ieee80211_mu_group_data {
*/
struct ieee80211_bss_conf {
const u8 *bssid;
u8 bss_color;
u8 htc_trig_based_pkt_ext;
bool multi_sta_back_32bit;
bool uora_exists;
bool ack_enabled;
u8 uora_ocw_range;
u16 frame_time_rts_th;
bool he_support;
/* association related data */
bool assoc, ibss_joined;
bool ibss_creator;
......@@ -1106,6 +1128,18 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
* @RX_FLAG_AMPDU_EOF_BIT: Value of the EOF bit in the A-MPDU delimiter for this
* frame
* @RX_FLAG_AMPDU_EOF_BIT_KNOWN: The EOF value is known
* @RX_FLAG_RADIOTAP_HE: HE radiotap data is present
* (&struct ieee80211_radiotap_he, mac80211 will fill in
* - DATA3_DATA_MCS
* - DATA3_DATA_DCM
* - DATA3_CODING
* - DATA5_GI
* - DATA5_DATA_BW_RU_ALLOC
* - DATA6_NSTS
* - DATA3_STBC
* from the RX info data, so leave those zeroed when building this data)
* @RX_FLAG_RADIOTAP_HE_MU: HE MU radiotap data is present
* (&struct ieee80211_radiotap_he_mu)
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
......@@ -1134,6 +1168,8 @@ enum mac80211_rx_flags {
RX_FLAG_ICV_STRIPPED = BIT(23),
RX_FLAG_AMPDU_EOF_BIT = BIT(24),
RX_FLAG_AMPDU_EOF_BIT_KNOWN = BIT(25),
RX_FLAG_RADIOTAP_HE = BIT(26),
RX_FLAG_RADIOTAP_HE_MU = BIT(27),
};
/**
......@@ -1164,6 +1200,7 @@ enum mac80211_rx_encoding {
RX_ENC_LEGACY = 0,
RX_ENC_HT,
RX_ENC_VHT,
RX_ENC_HE,
};
/**
......@@ -1198,6 +1235,9 @@ enum mac80211_rx_encoding {
* @encoding: &enum mac80211_rx_encoding
* @bw: &enum rate_info_bw
* @enc_flags: uses bits from &enum mac80211_rx_encoding_flags
* @he_ru: HE RU, from &enum nl80211_he_ru_alloc
* @he_gi: HE GI, from &enum nl80211_he_gi
* @he_dcm: HE DCM value
* @rx_flags: internal RX flags for mac80211
* @ampdu_reference: A-MPDU reference number, must be a different value for
* each A-MPDU but the same for each subframe within one A-MPDU
......@@ -1211,7 +1251,8 @@ struct ieee80211_rx_status {
u32 flag;
u16 freq;
u8 enc_flags;
u8 encoding:2, bw:3;
u8 encoding:2, bw:3, he_ru:3;
u8 he_gi:2, he_dcm:1;
u8 rate_idx;
u8 nss;
u8 rx_flags;
......@@ -1770,6 +1811,7 @@ struct ieee80211_sta_rates {
* @supp_rates: Bitmap of supported rates (per band)
* @ht_cap: HT capabilities of this STA; restricted to our own capabilities
* @vht_cap: VHT capabilities of this STA; restricted to our own capabilities
* @he_cap: HE capabilities of this STA
* @max_rx_aggregation_subframes: maximal amount of frames in a single AMPDU
* that this station is allowed to transmit to us.
* Can be modified by driver.
......@@ -1805,7 +1847,8 @@ struct ieee80211_sta {
u16 aid;
struct ieee80211_sta_ht_cap ht_cap;
struct ieee80211_sta_vht_cap vht_cap;
u8 max_rx_aggregation_subframes;
struct ieee80211_sta_he_cap he_cap;
u16 max_rx_aggregation_subframes;
bool wme;
u8 uapsd_queues;
u8 max_sp;
......@@ -2196,10 +2239,11 @@ enum ieee80211_hw_flags {
* it shouldn't be set.
*
* @max_tx_aggregation_subframes: maximum number of subframes in an
* aggregate an HT driver will transmit. Though ADDBA will advertise
* a constant value of 64 as some older APs can crash if the window
* size is smaller (an example is LinkSys WRT120N with FW v1.0.07
* build 002 Jun 18 2012).
* aggregate an HT/HE device will transmit. In HT AddBA we'll
* advertise a constant value of 64 as some older APs crash if
* the window size is smaller (an example is LinkSys WRT120N
* with FW v1.0.07 build 002 Jun 18 2012).
* For AddBA to HE capable peers this value will be used.
*
* @max_tx_fragments: maximum number of tx buffers per (A)-MSDU, sum
* of 1 + skb_shinfo(skb)->nr_frags for each skb in the frag_list.
......@@ -2216,6 +2260,8 @@ enum ieee80211_hw_flags {
* the default is _GI | _BANDWIDTH.
* Use the %IEEE80211_RADIOTAP_VHT_KNOWN_\* values.
*
* @radiotap_he: HE radiotap validity flags
*
* @radiotap_timestamp: Information for the radiotap timestamp field; if the
* 'units_pos' member is set to a non-negative value it must be set to
* a combination of a IEEE80211_RADIOTAP_TIMESTAMP_UNIT_* and a
......@@ -2263,8 +2309,8 @@ struct ieee80211_hw {
u8 max_rates;
u8 max_report_rates;
u8 max_rate_tries;
u8 max_rx_aggregation_subframes;
u8 max_tx_aggregation_subframes;
u16 max_rx_aggregation_subframes;
u16 max_tx_aggregation_subframes;
u8 max_tx_fragments;
u8 offchannel_tx_hw_queue;
u8 radiotap_mcs_details;
......@@ -2904,7 +2950,7 @@ struct ieee80211_ampdu_params {
struct ieee80211_sta *sta;
u16 tid;
u16 ssn;
u8 buf_size;
u16 buf_size;
bool amsdu;
u16 timeout;
};
......
......@@ -12,6 +12,7 @@ mac80211-y := \
scan.o offchannel.o \
ht.o agg-tx.o agg-rx.o \
vht.o \
he.o \
ibss.o \
iface.o \
rate.o \
......
......@@ -245,6 +245,7 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
};
int i, ret = -EOPNOTSUPP;
u16 status = WLAN_STATUS_REQUEST_DECLINED;
u16 max_buf_size;
if (tid >= IEEE80211_FIRST_TSPEC_TSID) {
ht_dbg(sta->sdata,
......@@ -268,13 +269,18 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
goto end;
}
if (sta->sta.he_cap.has_he)
max_buf_size = IEEE80211_MAX_AMPDU_BUF;
else
max_buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
/* sanity check for incoming parameters:
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
/* XXX: check own ht delayed BA capability?? */
if (((ba_policy != 1) &&
(!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) ||
(buf_size > IEEE80211_MAX_AMPDU_BUF_HT)) {
(buf_size > max_buf_size)) {
status = WLAN_STATUS_INVALID_QOS_PARAM;
ht_dbg_ratelimited(sta->sdata,
"AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n",
......@@ -283,7 +289,7 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
}
/* determine default buffer size */
if (buf_size == 0)
buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
buf_size = max_buf_size;
/* make sure the size doesn't exceed the maximum supported by the hw */
if (buf_size > sta->sta.max_rx_aggregation_subframes)
......
......@@ -463,6 +463,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
.timeout = 0,
};
int ret;
u16 buf_size;
tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
......@@ -511,11 +512,22 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
sta->ampdu_mlme.addba_req_num[tid]++;
spin_unlock_bh(&sta->lock);
if (sta->sta.he_cap.has_he) {
buf_size = local->hw.max_tx_aggregation_subframes;
} else {
/*
* We really should use what the driver told us it will
* transmit as the maximum, but certain APs (e.g. the
* LinkSys WRT120N with FW v1.0.07 build 002 Jun 18 2012)
* will crash when we use a lower number.
*/
buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
}
/* send AddBA request */
ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
tid_tx->dialog_token, params.ssn,
IEEE80211_MAX_AMPDU_BUF_HT,
tid_tx->timeout);
buf_size, tid_tx->timeout);
}
/*
......@@ -905,8 +917,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
{
struct tid_ampdu_tx *tid_tx;
struct ieee80211_txq *txq;
u16 capab, tid;
u8 buf_size;
u16 capab, tid, buf_size;
bool amsdu;
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
......
......@@ -1412,6 +1412,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
params->vht_capa, sta);
if (params->he_capa)
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
(void *)params->he_capa,
params->he_capa_len, sta);
if (params->opmode_notif_used) {
/* returned value is only needed for rc update, but the
* rc isn't initialized here yet, so ignore it
......
/*
* HE handling
*
* Copyright(c) 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "ieee80211_i.h"
void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const u8 *he_cap_ie, u8 he_cap_len,
struct sta_info *sta)
{
struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap;
struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
u8 he_ppe_size;
u8 mcs_nss_size;
u8 he_total_size;
memset(he_cap, 0, sizeof(*he_cap));
if (!he_cap_ie || !ieee80211_get_he_sta_cap(sband))
return;
/* Make sure size is OK */
mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap_ie_elem);
he_ppe_size =
ieee80211_he_ppe_size(he_cap_ie[sizeof(he_cap->he_cap_elem) +
mcs_nss_size],
he_cap_ie_elem->phy_cap_info);
he_total_size = sizeof(he_cap->he_cap_elem) + mcs_nss_size +
he_ppe_size;
if (he_cap_len < he_total_size)
return;
memcpy(&he_cap->he_cap_elem, he_cap_ie, sizeof(he_cap->he_cap_elem));
/* HE Tx/Rx HE MCS NSS Support Field */
memcpy(&he_cap->he_mcs_nss_supp,
&he_cap_ie[sizeof(he_cap->he_cap_elem)], mcs_nss_size);
/* Check if there are (optional) PPE Thresholds */
if (he_cap->he_cap_elem.phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT)
memcpy(he_cap->ppe_thres,
&he_cap_ie[sizeof(he_cap->he_cap_elem) + mcs_nss_size],
he_ppe_size);
he_cap->has_he = true;
}
......@@ -365,6 +365,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_DISABLE_160MHZ = BIT(13),
IEEE80211_STA_DISABLE_WMM = BIT(14),
IEEE80211_STA_ENABLE_RRM = BIT(15),
IEEE80211_STA_DISABLE_HE = BIT(16),
};
struct ieee80211_mgd_auth_data {
......@@ -1454,6 +1455,10 @@ struct ieee802_11_elems {
const struct ieee80211_vht_cap *vht_cap_elem;
const struct ieee80211_vht_operation *vht_operation;
const struct ieee80211_meshconf_ie *mesh_config;
const u8 *he_cap;
const struct ieee80211_he_operation *he_operation;
const struct ieee80211_mu_edca_param_set *mu_edca_param_set;
const u8 *uora_element;
const u8 *mesh_id;
const u8 *peering;
const __le16 *awake_window;
......@@ -1483,6 +1488,7 @@ struct ieee802_11_elems {
u8 ext_supp_rates_len;
u8 wmm_info_len;
u8 wmm_param_len;
u8 he_cap_len;
u8 mesh_id_len;
u8 peering_len;
u8 preq_len;
......@@ -1825,6 +1831,13 @@ void ieee80211_get_vht_mask_from_cap(__le16 vht_cap,
enum nl80211_chan_width
ieee80211_sta_rx_bw_to_chan_width(struct sta_info *sta);
/* HE */
void
ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
const u8 *he_cap_ie, u8 he_cap_len,
struct sta_info *sta);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
......@@ -2076,6 +2089,9 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
const struct cfg80211_chan_def *chandef);
u8 *ieee80211_ie_build_he_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end);
int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
const struct ieee80211_supported_band *sband,
const u8 *srates, int srates_len, u32 *rates);
......
......@@ -3,6 +3,7 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
......@@ -825,7 +826,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
int result, i;
enum nl80211_band band;
int channels, max_bitrates;
bool supp_ht, supp_vht;
bool supp_ht, supp_vht, supp_he;
netdev_features_t feature_whitelist;
struct cfg80211_chan_def dflt_chandef = {};
......@@ -905,6 +906,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
max_bitrates = 0;
supp_ht = false;
supp_vht = false;
supp_he = false;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband;
......@@ -931,6 +933,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
supp_ht = supp_ht || sband->ht_cap.ht_supported;
supp_vht = supp_vht || sband->vht_cap.vht_supported;
if (!supp_he)
supp_he = !!ieee80211_get_he_sta_cap(sband);
if (!sband->ht_cap.ht_supported)
continue;
......@@ -1020,6 +1025,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->scan_ies_len +=
2 + sizeof(struct ieee80211_vht_cap);
/* HE cap element is variable in size - set len to allow max size */
/*
* TODO: 1 is added at the end of the calculation to accommodate for
* the temporary placing of the HE capabilities IE under EXT.
* Remove it once it is placed in the final place.
*/
if (supp_he)
local->scan_ies_len +=
2 + sizeof(struct ieee80211_he_cap_elem) +
sizeof(struct ieee80211_he_mcs_nss_supp) +
IEEE80211_HE_PPE_THRES_MAX_LEN + 1;
if (!local->ops->hw_scan) {
/* For hw_scan, driver needs to set these up. */
local->hw.wiphy->max_scan_ssids = 4;
......
This diff is collapsed.
......@@ -175,6 +175,20 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
len += 12;
}
if (status->encoding == RX_ENC_HE &&
status->flag & RX_FLAG_RADIOTAP_HE) {
len = ALIGN(len, 2);
len += 12;
BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he) != 12);
}
if (status->encoding == RX_ENC_HE &&
status->flag & RX_FLAG_RADIOTAP_HE_MU) {
len = ALIGN(len, 2);
len += 12;
BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he_mu) != 12);
}
if (status->chains) {
/* antenna and antenna signal fields */
len += 2 * hweight8(status->chains);
......@@ -263,6 +277,19 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
int mpdulen, chain;
unsigned long chains = status->chains;
struct ieee80211_vendor_radiotap rtap = {};
struct ieee80211_radiotap_he he = {};
struct ieee80211_radiotap_he_mu he_mu = {};
if (status->flag & RX_FLAG_RADIOTAP_HE) {
he = *(struct ieee80211_radiotap_he *)skb->data;
skb_pull(skb, sizeof(he));
WARN_ON_ONCE(status->encoding != RX_ENC_HE);
}
if (status->flag & RX_FLAG_RADIOTAP_HE_MU) {
he_mu = *(struct ieee80211_radiotap_he_mu *)skb->data;
skb_pull(skb, sizeof(he_mu));
}
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
......@@ -520,6 +547,89 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = flags;
}
if (status->encoding == RX_ENC_HE &&
status->flag & RX_FLAG_RADIOTAP_HE) {
#define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
if (status->enc_flags & RX_ENC_FLAG_STBC_MASK) {
he.data6 |= HE_PREP(DATA6_NSTS,
FIELD_GET(RX_ENC_FLAG_STBC_MASK,
status->enc_flags));
he.data3 |= HE_PREP(DATA3_STBC, 1);
} else {
he.data6 |= HE_PREP(DATA6_NSTS, status->nss);
}
#define CHECK_GI(s) \
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_GI_##s != \
(int)NL80211_RATE_INFO_HE_GI_##s)
CHECK_GI(0_8);
CHECK_GI(1_6);
CHECK_GI(3_2);
he.data3 |= HE_PREP(DATA3_DATA_MCS, status->rate_idx);
he.data3 |= HE_PREP(DATA3_DATA_DCM, status->he_dcm);
he.data3 |= HE_PREP(DATA3_CODING,
!!(status->enc_flags & RX_ENC_FLAG_LDPC));
he.data5 |= HE_PREP(DATA5_GI, status->he_gi);
switch (status->bw) {
case RATE_INFO_BW_20:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
break;
case RATE_INFO_BW_40:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
break;
case RATE_INFO_BW_80:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
break;
case RATE_INFO_BW_160:
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
break;
case RATE_INFO_BW_HE_RU:
#define CHECK_RU_ALLOC(s) \
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_##s##T != \
NL80211_RATE_INFO_HE_RU_ALLOC_##s + 4)
CHECK_RU_ALLOC(26);
CHECK_RU_ALLOC(52);
CHECK_RU_ALLOC(106);
CHECK_RU_ALLOC(242);
CHECK_RU_ALLOC(484);
CHECK_RU_ALLOC(996);
CHECK_RU_ALLOC(2x996);
he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
status->he_ru + 4);
break;
default:
WARN_ONCE(1, "Invalid SU BW %d\n", status->bw);
}
/* ensure 2 byte alignment */
while ((pos - (u8 *)rthdr) & 1)
pos++;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
memcpy(pos, &he, sizeof(he));
pos += sizeof(he);
}
if (status->encoding == RX_ENC_HE &&
status->flag & RX_FLAG_RADIOTAP_HE_MU) {
/* ensure 2 byte alignment */
while ((pos - (u8 *)rthdr) & 1)
pos++;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE_MU);
memcpy(pos, &he_mu, sizeof(he_mu));
pos += sizeof(he_mu);
}
for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
*pos++ = status->chain_signal[chain];
*pos++ = chain;
......@@ -613,6 +723,12 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
rcu_dereference(local->monitor_sdata);
bool only_monitor = false;
if (status->flag & RX_FLAG_RADIOTAP_HE)
rtap_space += sizeof(struct ieee80211_radiotap_he);
if (status->flag & RX_FLAG_RADIOTAP_HE_MU)
rtap_space += sizeof(struct ieee80211_radiotap_he_mu);
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
......@@ -3386,8 +3502,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
status = IEEE80211_SKB_RXCB((rx->skb));
sband = rx->local->hw.wiphy->bands[status->band];
if (!(status->encoding == RX_ENC_HT) &&
!(status->encoding == RX_ENC_VHT))
if (status->encoding == RX_ENC_LEGACY)
rate = &sband->bitrates[status->rate_idx];
ieee80211_rx_cooked_monitor(rx, rate);
......@@ -4386,6 +4501,14 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
status->rate_idx, status->nss))
goto drop;
break;
case RX_ENC_HE:
if (WARN_ONCE(status->rate_idx > 11 ||
!status->nss ||
status->nss > 8,
"Rate marked as an HE rate but data is invalid: MCS: %d, NSS: %d\n",
status->rate_idx, status->nss))
goto drop;
break;
default:
WARN_ON_ONCE(1);
/* fall through */
......
......@@ -1323,6 +1323,11 @@ static void ieee80211_send_null_response(struct sta_info *sta, int tid,
struct ieee80211_tx_info *info;
struct ieee80211_chanctx_conf *chanctx_conf;
/* Don't send NDPs when STA is connected HE */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE))
return;
if (qos) {
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_QOS_NULLFUNC |
......@@ -1968,7 +1973,7 @@ sta_get_last_rx_stats(struct sta_info *sta)
return stats;
}
static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
struct rate_info *rinfo)
{
rinfo->bw = STA_STATS_GET(BW, rate);
......@@ -2005,6 +2010,14 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
break;
}
case STA_STATS_RATE_TYPE_HE:
rinfo->flags = RATE_INFO_FLAGS_HE_MCS;
rinfo->mcs = STA_STATS_GET(HE_MCS, rate);
rinfo->nss = STA_STATS_GET(HE_NSS, rate);
rinfo->he_gi = STA_STATS_GET(HE_GI, rate);
rinfo->he_ru_alloc = STA_STATS_GET(HE_RU, rate);
rinfo->he_dcm = STA_STATS_GET(HE_DCM, rate);
break;
}
}
......
......@@ -170,7 +170,7 @@ struct tid_ampdu_tx {
u8 dialog_token;
u8 stop_initiator;
bool tx_stop;
u8 buf_size;
u16 buf_size;
u16 failed_bar_ssn;
bool bar_pending;
......@@ -405,7 +405,7 @@ struct ieee80211_sta_rx_stats {
int last_signal;
u8 chains;
s8 chain_signal_last[IEEE80211_MAX_CHAINS];
u16 last_rate;
u32 last_rate;
struct u64_stats_sync syncp;
u64 bytes;
u64 msdu[IEEE80211_NUM_TIDS + 1];
......@@ -764,6 +764,7 @@ enum sta_stats_type {
STA_STATS_RATE_TYPE_LEGACY,
STA_STATS_RATE_TYPE_HT,
STA_STATS_RATE_TYPE_VHT,
STA_STATS_RATE_TYPE_HE,
};
#define STA_STATS_FIELD_HT_MCS GENMASK( 7, 0)
......@@ -771,9 +772,14 @@ enum sta_stats_type {
#define STA_STATS_FIELD_LEGACY_BAND GENMASK( 7, 4)
#define STA_STATS_FIELD_VHT_MCS GENMASK( 3, 0)
#define STA_STATS_FIELD_VHT_NSS GENMASK( 7, 4)
#define STA_STATS_FIELD_HE_MCS GENMASK( 3, 0)
#define STA_STATS_FIELD_HE_NSS GENMASK( 7, 4)
#define STA_STATS_FIELD_BW GENMASK(11, 8)
#define STA_STATS_FIELD_SGI GENMASK(12, 12)
#define STA_STATS_FIELD_TYPE GENMASK(15, 13)
#define STA_STATS_FIELD_HE_RU GENMASK(18, 16)
#define STA_STATS_FIELD_HE_GI GENMASK(20, 19)
#define STA_STATS_FIELD_HE_DCM GENMASK(21, 21)
#define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v)
#define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v)
......@@ -782,7 +788,7 @@ enum sta_stats_type {
static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
{
u16 r;
u32 r;
r = STA_STATS_FIELD(BW, s->bw);
......@@ -804,6 +810,14 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
r |= STA_STATS_FIELD(LEGACY_BAND, s->band);
r |= STA_STATS_FIELD(LEGACY_IDX, s->rate_idx);
break;
case RX_ENC_HE:
r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_HE);
r |= STA_STATS_FIELD(HE_NSS, s->nss);
r |= STA_STATS_FIELD(HE_MCS, s->rate_idx);
r |= STA_STATS_FIELD(HE_GI, s->he_gi);
r |= STA_STATS_FIELD(HE_RU, s->he_ru);
r |= STA_STATS_FIELD(HE_DCM, s->he_dcm);
break;
default:
WARN_ON(1);
return STA_STATS_RATE_INVALID;
......
......@@ -92,7 +92,7 @@
STA_ENTRY \
__field(u16, tid) \
__field(u16, ssn) \
__field(u8, buf_size) \
__field(u16, buf_size) \
__field(bool, amsdu) \
__field(u16, timeout) \
__field(u16, action)
......
......@@ -1095,6 +1095,21 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
if (elen >= sizeof(*elems->max_idle_period_ie))
elems->max_idle_period_ie = (void *)pos;
break;
case WLAN_EID_EXTENSION:
if (pos[0] == WLAN_EID_EXT_HE_MU_EDCA &&
elen >= (sizeof(*elems->mu_edca_param_set) + 1)) {
elems->mu_edca_param_set = (void *)&pos[1];
} else if (pos[0] == WLAN_EID_EXT_HE_CAPABILITY) {
elems->he_cap = (void *)&pos[1];
elems->he_cap_len = elen - 1;
} else if (pos[0] == WLAN_EID_EXT_HE_OPERATION &&
elen >= sizeof(*elems->he_operation) &&
elen >= ieee80211_he_oper_size(&pos[1])) {
elems->he_operation = (void *)&pos[1];
} else if (pos[0] == WLAN_EID_EXT_UORA && elen >= 1) {
elems->uora_element = (void *)&pos[1];
}
break;
default:
break;
}
......@@ -1356,6 +1371,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
size_t *offset, u32 flags)
{
struct ieee80211_supported_band *sband;
const struct ieee80211_sta_he_cap *he_cap;
u8 *pos = buffer, *end = buffer + buffer_len;
size_t noffset;
int supp_rates_len, i;
......@@ -1463,11 +1479,6 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
sband->ht_cap.cap);
}
/*
* If adding more here, adjust code in main.c
* that calculates local->scan_ies_len.
*/
/* insert custom IEs that go before VHT */
if (ie && ie_len) {
static const u8 before_vht[] = {
......@@ -1510,6 +1521,39 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
sband->vht_cap.cap);
}
/* insert custom IEs that go before HE */
if (ie && ie_len) {
static const u8 before_he[] = {
/*
* no need to list the ones split off before VHT
* or generated here
*/
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_REQ_PARAMS,
WLAN_EID_AP_CSN,
/* TODO: add 11ah/11aj/11ak elements */
};
noffset = ieee80211_ie_split(ie, ie_len,
before_he, ARRAY_SIZE(before_he),
*offset);
if (end - pos < noffset - *offset)
goto out_err;
memcpy(pos, ie + *offset, noffset - *offset);
pos += noffset - *offset;
*offset = noffset;
}
he_cap = ieee80211_get_he_sta_cap(sband);
if (he_cap) {
pos = ieee80211_ie_build_he_cap(pos, he_cap, end);
if (!pos)
goto out_err;
}
/*
* If adding more here, adjust code in main.c
* that calculates local->scan_ies_len.
*/
return pos - buffer;
out_err:
WARN_ONCE(1, "not enough space for preq IEs\n");
......@@ -2396,6 +2440,72 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
return pos;
}
u8 *ieee80211_ie_build_he_cap(u8 *pos,
const struct ieee80211_sta_he_cap *he_cap,
u8 *end)
{
u8 n;
u8 ie_len;
u8 *orig_pos = pos;
/* Make sure we have place for the IE */
/*
* TODO: the 1 added is because this temporarily is under the EXTENSION
* IE. Get rid of it when it moves.
*/
if (!he_cap)
return orig_pos;
n = ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem);
ie_len = 2 + 1 +
sizeof(he_cap->he_cap_elem) + n +
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
if ((end - pos) < ie_len)
return orig_pos;
*pos++ = WLAN_EID_EXTENSION;
pos++; /* We'll set the size later below */
*pos++ = WLAN_EID_EXT_HE_CAPABILITY;
/* Fixed data */
memcpy(pos, &he_cap->he_cap_elem, sizeof(he_cap->he_cap_elem));
pos += sizeof(he_cap->he_cap_elem);
memcpy(pos, &he_cap->he_mcs_nss_supp, n);
pos += n;
/* Check if PPE Threshold should be present */
if ((he_cap->he_cap_elem.phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) == 0)
goto end;
/*
* Calculate how many PPET16/PPET8 pairs are to come. Algorithm:
* (NSS_M1 + 1) x (num of 1 bits in RU_INDEX_BITMASK)
*/
n = hweight8(he_cap->ppe_thres[0] &
IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK);
n *= (1 + ((he_cap->ppe_thres[0] & IEEE80211_PPE_THRES_NSS_MASK) >>
IEEE80211_PPE_THRES_NSS_POS));
/*
* Each pair is 6 bits, and we need to add the 7 "header" bits to the
* total size.
*/
n = (n * IEEE80211_PPE_THRES_INFO_PPET_SIZE * 2) + 7;
n = DIV_ROUND_UP(n, 8);
/* Copy PPE Thresholds */
memcpy(pos, &he_cap->ppe_thres, n);
pos += n;
end:
orig_pos[1] = (pos - orig_pos) - 2;
return pos;
}
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
const struct cfg80211_chan_def *chandef,
u16 prot_mode, bool rifs_mode)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment