Skip to content

Commit

Permalink
mac80211: handle VHT operating mode notification
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Berg committed Feb 15, 2013
1 parent 8921d04 commit 0af83d3
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 10 deletions.
1 change: 1 addition & 0 deletions include/linux/ieee80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions include/net/mac80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};

/**
Expand Down
4 changes: 4 additions & 0 deletions net/mac80211/ht.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
3 changes: 3 additions & 0 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
30 changes: 30 additions & 0 deletions net/mac80211/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
4 changes: 4 additions & 0 deletions net/mac80211/sta_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;

Expand Down
93 changes: 83 additions & 10 deletions net/mac80211/vht.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -39,19 +40,29 @@ 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);
}

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:
Expand All @@ -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)
Expand Down Expand Up @@ -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);
}

0 comments on commit 0af83d3

Please sign in to comment.