aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-12-27 12:55:36 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-15 03:41:32 -0500
commit0af83d3df5863224336a18c24a14fda542b712f5 (patch)
treefa6a365edab208a78941ef80084afc0aaca87813
parent8921d04e8df7475d733d853564bdb001e83bf33f (diff)
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>
-rw-r--r--include/linux/ieee80211.h1
-rw-r--r--include/net/mac80211.h3
-rw-r--r--net/mac80211/ht.c4
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/rx.c30
-rw-r--r--net/mac80211/sta_info.h4
-rw-r--r--net/mac80211/vht.c93
7 files changed, 128 insertions, 10 deletions
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index 67c1a6c45837..12b5996533ec 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1301,6 +1301,7 @@ struct ieee80211_vht_operation {
1301#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002 1301#define IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 0x00000002
1302#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004 1302#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004
1303#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008 1303#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008
1304#define IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK 0x0000000C
1304#define IEEE80211_VHT_CAP_RXLDPC 0x00000010 1305#define IEEE80211_VHT_CAP_RXLDPC 0x00000010
1305#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020 1306#define IEEE80211_VHT_CAP_SHORT_GI_80 0x00000020
1306#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040 1307#define IEEE80211_VHT_CAP_SHORT_GI_160 0x00000040
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index a608ab9879b4..b7fb311e83f4 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2118,11 +2118,14 @@ enum ieee80211_frame_release_type {
2118 * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer 2118 * @IEEE80211_RC_SUPP_RATES_CHANGED: The supported rate set of this peer
2119 * changed (in IBSS mode) due to discovering more information about 2119 * changed (in IBSS mode) due to discovering more information about
2120 * the peer. 2120 * the peer.
2121 * @IEEE80211_RC_NSS_CHANGED: N_SS (number of spatial streams) was changed
2122 * by the peer
2121 */ 2123 */
2122enum ieee80211_rate_control_changed { 2124enum ieee80211_rate_control_changed {
2123 IEEE80211_RC_BW_CHANGED = BIT(0), 2125 IEEE80211_RC_BW_CHANGED = BIT(0),
2124 IEEE80211_RC_SMPS_CHANGED = BIT(1), 2126 IEEE80211_RC_SMPS_CHANGED = BIT(1),
2125 IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2), 2127 IEEE80211_RC_SUPP_RATES_CHANGED = BIT(2),
2128 IEEE80211_RC_NSS_CHANGED = BIT(3),
2126}; 2129};
2127 2130
2128/** 2131/**
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index a64b4f0d373f..797969bc26e1 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -212,6 +212,10 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
212 changed = true; 212 changed = true;
213 sta->sta.bandwidth = bw; 213 sta->sta.bandwidth = bw;
214 214
215 sta->cur_max_bandwidth =
216 ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
217 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
218
215 return changed; 219 return changed;
216} 220}
217 221
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 3b13af4e6c49..4947c91c6c86 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1433,6 +1433,9 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
1433 struct sta_info *sta); 1433 struct sta_info *sta);
1434enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); 1434enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta);
1435void ieee80211_sta_set_rx_nss(struct sta_info *sta); 1435void ieee80211_sta_set_rx_nss(struct sta_info *sta);
1436void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
1437 struct sta_info *sta, u8 opmode,
1438 enum ieee80211_band band);
1436 1439
1437/* Spectrum management */ 1440/* Spectrum management */
1438void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, 1441void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 8a861a50b12f..1617e0bd4ca6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2436,6 +2436,36 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
2436 } 2436 }
2437 2437
2438 break; 2438 break;
2439 case WLAN_CATEGORY_VHT:
2440 if (sdata->vif.type != NL80211_IFTYPE_STATION &&
2441 sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
2442 sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
2443 sdata->vif.type != NL80211_IFTYPE_AP &&
2444 sdata->vif.type != NL80211_IFTYPE_ADHOC)
2445 break;
2446
2447 /* verify action code is present */
2448 if (len < IEEE80211_MIN_ACTION_SIZE + 1)
2449 goto invalid;
2450
2451 switch (mgmt->u.action.u.vht_opmode_notif.action_code) {
2452 case WLAN_VHT_ACTION_OPMODE_NOTIF: {
2453 u8 opmode;
2454
2455 /* verify opmode is present */
2456 if (len < IEEE80211_MIN_ACTION_SIZE + 2)
2457 goto invalid;
2458
2459 opmode = mgmt->u.action.u.vht_opmode_notif.operating_mode;
2460
2461 ieee80211_vht_handle_opmode(rx->sdata, rx->sta,
2462 opmode, status->band);
2463 goto handled;
2464 }
2465 default:
2466 break;
2467 }
2468 break;
2439 case WLAN_CATEGORY_BACK: 2469 case WLAN_CATEGORY_BACK:
2440 if (sdata->vif.type != NL80211_IFTYPE_STATION && 2470 if (sdata->vif.type != NL80211_IFTYPE_STATION &&
2441 sdata->vif.type != NL80211_IFTYPE_MESH_POINT && 2471 sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 03c42f8d39f0..63dfdb5e91da 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -297,6 +297,8 @@ struct sta_ampdu_mlme {
297 * @sta_state: duplicates information about station state (for debug) 297 * @sta_state: duplicates information about station state (for debug)
298 * @beacon_loss_count: number of times beacon loss has triggered 298 * @beacon_loss_count: number of times beacon loss has triggered
299 * @rcu_head: RCU head used for freeing this station struct 299 * @rcu_head: RCU head used for freeing this station struct
300 * @cur_max_bandwidth: maximum bandwidth to use for TX to the station,
301 * taken from HT/VHT capabilities or VHT operating mode notification
300 */ 302 */
301struct sta_info { 303struct sta_info {
302 /* General information, mostly static */ 304 /* General information, mostly static */
@@ -398,6 +400,8 @@ struct sta_info {
398 } debugfs; 400 } debugfs;
399#endif 401#endif
400 402
403 enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
404
401 unsigned int lost_packets; 405 unsigned int lost_packets;
402 unsigned int beacon_loss_count; 406 unsigned int beacon_loss_count;
403 407
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 67436e3efbbd..0951f74e7ff5 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -10,6 +10,7 @@
10#include <linux/export.h> 10#include <linux/export.h>
11#include <net/mac80211.h> 11#include <net/mac80211.h>
12#include "ieee80211_i.h" 12#include "ieee80211_i.h"
13#include "rate.h"
13 14
14 15
15void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, 16void 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,
39 memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, 40 memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
40 sizeof(struct ieee80211_vht_mcs_info)); 41 sizeof(struct ieee80211_vht_mcs_info));
41 42
43 switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
44 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
45 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
46 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
47 break;
48 default:
49 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
50 }
51
42 sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); 52 sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta);
43} 53}
44 54
@@ -46,12 +56,13 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
46{ 56{
47 struct ieee80211_sub_if_data *sdata = sta->sdata; 57 struct ieee80211_sub_if_data *sdata = sta->sdata;
48 u32 cap = sta->sta.vht_cap.cap; 58 u32 cap = sta->sta.vht_cap.cap;
59 enum ieee80211_sta_rx_bandwidth bw;
49 60
50 if (!sta->sta.vht_cap.vht_supported) 61 if (!sta->sta.vht_cap.vht_supported) {
51 return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 62 bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
52 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 63 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
53 64 goto check_max;
54 /* TODO: handle VHT opmode notification data */ 65 }
55 66
56 switch (sdata->vif.bss_conf.chandef.width) { 67 switch (sdata->vif.bss_conf.chandef.width) {
57 default: 68 default:
@@ -60,19 +71,31 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
60 case NL80211_CHAN_WIDTH_20_NOHT: 71 case NL80211_CHAN_WIDTH_20_NOHT:
61 case NL80211_CHAN_WIDTH_20: 72 case NL80211_CHAN_WIDTH_20:
62 case NL80211_CHAN_WIDTH_40: 73 case NL80211_CHAN_WIDTH_40:
63 return sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 74 bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
64 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 75 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
76 break;
65 case NL80211_CHAN_WIDTH_160: 77 case NL80211_CHAN_WIDTH_160:
66 if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) 78 if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
67 return IEEE80211_STA_RX_BW_160; 79 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) {
80 bw = IEEE80211_STA_RX_BW_160;
81 break;
82 }
68 /* fall through */ 83 /* fall through */
69 case NL80211_CHAN_WIDTH_80P80: 84 case NL80211_CHAN_WIDTH_80P80:
70 if (cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) 85 if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) ==
71 return IEEE80211_STA_RX_BW_160; 86 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
87 bw = IEEE80211_STA_RX_BW_160;
88 break;
89 }
72 /* fall through */ 90 /* fall through */
73 case NL80211_CHAN_WIDTH_80: 91 case NL80211_CHAN_WIDTH_80:
74 return IEEE80211_STA_RX_BW_80; 92 bw = IEEE80211_STA_RX_BW_80;
75 } 93 }
94
95 check_max:
96 if (bw > sta->cur_max_bandwidth)
97 bw = sta->cur_max_bandwidth;
98 return bw;
76} 99}
77 100
78void ieee80211_sta_set_rx_nss(struct sta_info *sta) 101void ieee80211_sta_set_rx_nss(struct sta_info *sta)
@@ -115,3 +138,53 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)
115 ht_rx_nss = max(ht_rx_nss, vht_rx_nss); 138 ht_rx_nss = max(ht_rx_nss, vht_rx_nss);
116 sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss); 139 sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss);
117} 140}
141
142void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
143 struct sta_info *sta, u8 opmode,
144 enum ieee80211_band band)
145{
146 struct ieee80211_local *local = sdata->local;
147 struct ieee80211_supported_band *sband;
148 enum ieee80211_sta_rx_bandwidth new_bw;
149 u32 changed = 0;
150 u8 nss;
151
152 sband = local->hw.wiphy->bands[band];
153
154 /* ignore - no support for BF yet */
155 if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)
156 return;
157
158 nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
159 nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
160 nss += 1;
161
162 if (sta->sta.rx_nss != nss) {
163 sta->sta.rx_nss = nss;
164 changed |= IEEE80211_RC_NSS_CHANGED;
165 }
166
167 switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) {
168 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ:
169 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20;
170 break;
171 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ:
172 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40;
173 break;
174 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ:
175 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
176 break;
177 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ:
178 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160;
179 break;
180 }
181
182 new_bw = ieee80211_sta_cur_vht_bw(sta);
183 if (new_bw != sta->sta.bandwidth) {
184 sta->sta.bandwidth = new_bw;
185 changed |= IEEE80211_RC_NSS_CHANGED;
186 }
187
188 if (changed)
189 rate_control_rate_update(local, sband, sta, changed);
190}