mac80211: handle VHT operating mode notification
Handle the operating mode notification action frame. When the supported streams or the bandwidth change let the driver and rate control algorithm know. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
8921d04e8d
commit
0af83d3df5
7 changed files with 128 additions and 10 deletions
|
@ -1301,6 +1301,7 @@ struct ieee80211_vht_operation {
|
|||
#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002
|
||||
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004
|
||||
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008
|
||||
#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000C
|
||||
#define IEEE80211_VHT_CAP_RXLDPC 0x00000010
|
||||
#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020
|
||||
#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040
|
||||
|
|
|
@ -2118,11 +2118,14 @@ enum ieee80211_frame_release_type {
|
|||
* @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer
|
||||
* changed (in IBSS mode) due to discovering more information about
|
||||
* the peer.
|
||||
* @IEEE80211_RC_NSS_CHANGED: N_SS (number of spatial streams) was changed
|
||||
* by the peer
|
||||
*/
|
||||
enum ieee80211_rate_control_changed {
|
||||
IEEE80211_RC_BW_CHANGED = BIT(0),
|
||||
IEEE80211_RC_SMPS_CHANGED = BIT(1),
|
||||
IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2),
|
||||
IEEE80211_RC_NSS_CHANGED = BIT(3),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -212,6 +212,10 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
changed = true;
|
||||
sta->sta.bandwidth = bw;
|
||||
|
||||
sta->cur_max_bandwidth =
|
||||
ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
|
|
@ -1433,6 +1433,9 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
struct sta_info *sta);
|
||||
enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
|
||||
void ieee80211_sta_set_rx_nss(struct sta_info *sta);
|
||||
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta, u8 opmode,
|
||||
enum ieee80211_band band);
|
||||
|
||||
/* Spectrum management */
|
||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
@ -2435,6 +2435,36 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
|||
goto invalid;
|
||||
}
|
||||
|
||||
break;
|
||||
case WLAN_CATEGORY_VHT:
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
||||
sdata->vif.type != NL80211_IFTYPE_AP &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
break;
|
||||
|
||||
/* verify action code is present */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
goto invalid;
|
||||
|
||||
switch (mgmt->u.action.u.vht_opmode_notif.action_code) {
|
||||
case WLAN_VHT_ACTION_OPMODE_NOTIF: {
|
||||
u8 opmode;
|
||||
|
||||
/* verify opmode is present */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 2)
|
||||
goto invalid;
|
||||
|
||||
opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
|
||||
|
||||
ieee80211_vht_handle_opmode(rx->sdata, rx->sta,
|
||||
opmode, status->band);
|
||||
goto handled;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WLAN_CATEGORY_BACK:
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
|
|
|
@ -297,6 +297,8 @@ struct sta_ampdu_mlme {
|
|||
* @sta_state: duplicates information about station state (for debug)
|
||||
* @beacon_loss_count: number of times beacon loss has triggered
|
||||
* @rcu_head: RCU head used for freeing this station struct
|
||||
* @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
|
||||
* taken from HT/VHT capabilities or VHT operating mode notification
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
|
@ -398,6 +400,8 @@ struct sta_info {
|
|||
} debugfs;
|
||||
#endif
|
||||
|
||||
enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
|
||||
|
||||
unsigned int lost_packets;
|
||||
unsigned int beacon_loss_count;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "rate.h"
|
||||
|
||||
|
||||
void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -39,6 +40,15 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
|||
memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
|
||||
sizeof(struct ieee80211_vht_mcs_info));
|
||||
|
||||
switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
|
||||
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
|
||||
case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
default:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
|
||||
sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
|
||||
}
|
||||
|
||||
|
@ -46,12 +56,13 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
|
|||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
u32 cap = sta->sta.vht_cap.cap;
|
||||
enum ieee80211_sta_rx_bandwidth bw;
|
||||
|
||||
if (!sta->sta.vht_cap.vht_supported)
|
||||
return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
if (!sta->sta.vht_cap.vht_supported) {
|
||||
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
|
||||
/* TODO: handle VHT opmode notification data */
|
||||
goto check_max;
|
||||
}
|
||||
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
default:
|
||||
|
@ -60,19 +71,31 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
|
|||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
|
||||
bw = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return IEEE80211_STA_RX_BW_160;
|
||||
if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
|
||||
IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
|
||||
bw = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
return IEEE80211_STA_RX_BW_80;
|
||||
bw = IEEE80211_STA_RX_BW_80;
|
||||
}
|
||||
|
||||
check_max:
|
||||
if (bw > sta->cur_max_bandwidth)
|
||||
bw = sta->cur_max_bandwidth;
|
||||
return bw;
|
||||
}
|
||||
|
||||
void ieee80211_sta_set_rx_nss(struct sta_info *sta)
|
||||
|
@ -115,3 +138,53 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)
|
|||
ht_rx_nss = max(ht_rx_nss, vht_rx_nss);
|
||||
sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
|
||||
}
|
||||
|
||||
void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta, u8 opmode,
|
||||
enum ieee80211_band band)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_supported_band *sband;
|
||||
enum ieee80211_sta_rx_bandwidth new_bw;
|
||||
u32 changed = 0;
|
||||
u8 nss;
|
||||
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
|
||||
/* ignore - no support for BF yet */
|
||||
if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
|
||||
return;
|
||||
|
||||
nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
|
||||
nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
|
||||
nss += 1;
|
||||
|
||||
if (sta->sta.rx_nss != nss) {
|
||||
sta->sta.rx_nss = nss;
|
||||
changed |= IEEE80211_RC_NSS_CHANGED;
|
||||
}
|
||||
|
||||
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
|
||||
break;
|
||||
case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
|
||||
sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
|
||||
break;
|
||||
}
|
||||
|
||||
new_bw = ieee80211_sta_cur_vht_bw(sta);
|
||||
if (new_bw != sta->sta.bandwidth) {
|
||||
sta->sta.bandwidth = new_bw;
|
||||
changed |= IEEE80211_RC_NSS_CHANGED;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
rate_control_rate_update(local, sband, sta, changed);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue