Commit 0af83d3d authored by Johannes Berg's avatar Johannes Berg

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: default avatarJohannes Berg <johannes.berg@intel.com>
parent 8921d04e
......@@ -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);
}
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