diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-12-27 12:55:36 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-02-15 03:41:32 -0500 |
commit | 0af83d3df5863224336a18c24a14fda542b712f5 (patch) | |
tree | fa6a365edab208a78941ef80084afc0aaca87813 /net/mac80211 | |
parent | 8921d04e8df7475d733d853564bdb001e83bf33f (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>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/ht.c | 4 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/rx.c | 30 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 4 | ||||
-rw-r--r-- | net/mac80211/vht.c | 93 |
5 files changed, 124 insertions, 10 deletions
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); |
1434 | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); | 1434 | enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta); |
1435 | void ieee80211_sta_set_rx_nss(struct sta_info *sta); | 1435 | void ieee80211_sta_set_rx_nss(struct sta_info *sta); |
1436 | void 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 */ |
1438 | void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, | 1441 | void 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 | */ |
301 | struct sta_info { | 303 | struct 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 | ||
15 | void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | 16 | 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, | |||
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 | ||
78 | void ieee80211_sta_set_rx_nss(struct sta_info *sta) | 101 | void 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 | |||
142 | void 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 | } | ||