diff options
Diffstat (limited to 'net/mac80211/vht.c')
-rw-r--r-- | net/mac80211/vht.c | 212 |
1 files changed, 211 insertions, 1 deletions
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index a2c2258bc84e..171344d4eb7c 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c | |||
@@ -13,6 +13,104 @@ | |||
13 | #include "rate.h" | 13 | #include "rate.h" |
14 | 14 | ||
15 | 15 | ||
16 | static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, | ||
17 | struct ieee80211_sta_vht_cap *vht_cap, | ||
18 | u32 flag) | ||
19 | { | ||
20 | __le32 le_flag = cpu_to_le32(flag); | ||
21 | |||
22 | if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && | ||
23 | !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) | ||
24 | vht_cap->cap &= ~flag; | ||
25 | } | ||
26 | |||
27 | void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, | ||
28 | struct ieee80211_sta_vht_cap *vht_cap) | ||
29 | { | ||
30 | int i; | ||
31 | u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; | ||
32 | |||
33 | if (!vht_cap->vht_supported) | ||
34 | return; | ||
35 | |||
36 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
37 | return; | ||
38 | |||
39 | __check_vhtcap_disable(sdata, vht_cap, | ||
40 | IEEE80211_VHT_CAP_RXLDPC); | ||
41 | __check_vhtcap_disable(sdata, vht_cap, | ||
42 | IEEE80211_VHT_CAP_SHORT_GI_80); | ||
43 | __check_vhtcap_disable(sdata, vht_cap, | ||
44 | IEEE80211_VHT_CAP_SHORT_GI_160); | ||
45 | __check_vhtcap_disable(sdata, vht_cap, | ||
46 | IEEE80211_VHT_CAP_TXSTBC); | ||
47 | __check_vhtcap_disable(sdata, vht_cap, | ||
48 | IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); | ||
49 | __check_vhtcap_disable(sdata, vht_cap, | ||
50 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); | ||
51 | __check_vhtcap_disable(sdata, vht_cap, | ||
52 | IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); | ||
53 | __check_vhtcap_disable(sdata, vht_cap, | ||
54 | IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); | ||
55 | |||
56 | /* Allow user to decrease AMPDU length exponent */ | ||
57 | if (sdata->u.mgd.vht_capa_mask.vht_cap_info & | ||
58 | cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { | ||
59 | u32 cap, n; | ||
60 | |||
61 | n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & | ||
62 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; | ||
63 | n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; | ||
64 | cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; | ||
65 | cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; | ||
66 | |||
67 | if (n < cap) { | ||
68 | vht_cap->cap &= | ||
69 | ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; | ||
70 | vht_cap->cap |= | ||
71 | n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | /* Allow the user to decrease MCSes */ | ||
76 | rxmcs_mask = | ||
77 | le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); | ||
78 | rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); | ||
79 | rxmcs_n &= rxmcs_mask; | ||
80 | rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); | ||
81 | |||
82 | txmcs_mask = | ||
83 | le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); | ||
84 | txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); | ||
85 | txmcs_n &= txmcs_mask; | ||
86 | txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); | ||
87 | for (i = 0; i < 8; i++) { | ||
88 | u8 m, n, c; | ||
89 | |||
90 | m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
91 | n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
92 | c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
93 | |||
94 | if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || | ||
95 | n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { | ||
96 | rxmcs_cap &= ~(3 << 2*i); | ||
97 | rxmcs_cap |= (rxmcs_n & (3 << 2*i)); | ||
98 | } | ||
99 | |||
100 | m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
101 | n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
102 | c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
103 | |||
104 | if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || | ||
105 | n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { | ||
106 | txmcs_cap &= ~(3 << 2*i); | ||
107 | txmcs_cap |= (txmcs_n & (3 << 2*i)); | ||
108 | } | ||
109 | } | ||
110 | vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); | ||
111 | vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); | ||
112 | } | ||
113 | |||
16 | void | 114 | void |
17 | ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | 115 | ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, |
18 | struct ieee80211_supported_band *sband, | 116 | struct ieee80211_supported_band *sband, |
@@ -20,6 +118,8 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | |||
20 | struct sta_info *sta) | 118 | struct sta_info *sta) |
21 | { | 119 | { |
22 | struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; | 120 | struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; |
121 | struct ieee80211_sta_vht_cap own_cap; | ||
122 | u32 cap_info, i; | ||
23 | 123 | ||
24 | memset(vht_cap, 0, sizeof(*vht_cap)); | 124 | memset(vht_cap, 0, sizeof(*vht_cap)); |
25 | 125 | ||
@@ -35,12 +135,122 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, | |||
35 | 135 | ||
36 | vht_cap->vht_supported = true; | 136 | vht_cap->vht_supported = true; |
37 | 137 | ||
38 | vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); | 138 | own_cap = sband->vht_cap; |
139 | /* | ||
140 | * If user has specified capability overrides, take care | ||
141 | * of that if the station we're setting up is the AP that | ||
142 | * we advertised a restricted capability set to. Override | ||
143 | * our own capabilities and then use those below. | ||
144 | */ | ||
145 | if (sdata->vif.type == NL80211_IFTYPE_STATION && | ||
146 | !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) | ||
147 | ieee80211_apply_vhtcap_overrides(sdata, &own_cap); | ||
148 | |||
149 | /* take some capabilities as-is */ | ||
150 | cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); | ||
151 | vht_cap->cap = cap_info; | ||
152 | vht_cap->cap &= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895 | | ||
153 | IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | | ||
154 | IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | | ||
155 | IEEE80211_VHT_CAP_RXLDPC | | ||
156 | IEEE80211_VHT_CAP_VHT_TXOP_PS | | ||
157 | IEEE80211_VHT_CAP_HTC_VHT | | ||
158 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | | ||
159 | IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | | ||
160 | IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | | ||
161 | IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | | ||
162 | IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; | ||
163 | |||
164 | /* and some based on our own capabilities */ | ||
165 | switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { | ||
166 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: | ||
167 | vht_cap->cap |= cap_info & | ||
168 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; | ||
169 | break; | ||
170 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: | ||
171 | vht_cap->cap |= cap_info & | ||
172 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; | ||
173 | break; | ||
174 | default: | ||
175 | /* nothing */ | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | /* symmetric capabilities */ | ||
180 | vht_cap->cap |= cap_info & own_cap.cap & | ||
181 | (IEEE80211_VHT_CAP_SHORT_GI_80 | | ||
182 | IEEE80211_VHT_CAP_SHORT_GI_160); | ||
183 | |||
184 | /* remaining ones */ | ||
185 | if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) { | ||
186 | vht_cap->cap |= cap_info & | ||
187 | (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | | ||
188 | IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX | | ||
189 | IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX); | ||
190 | } | ||
191 | |||
192 | if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) | ||
193 | vht_cap->cap |= cap_info & | ||
194 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; | ||
195 | |||
196 | if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) | ||
197 | vht_cap->cap |= cap_info & | ||
198 | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; | ||
199 | |||
200 | if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) | ||
201 | vht_cap->cap |= cap_info & | ||
202 | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; | ||
203 | |||
204 | if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) | ||
205 | vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; | ||
206 | |||
207 | if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) | ||
208 | vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; | ||
39 | 209 | ||
40 | /* Copy peer MCS info, the driver might need them. */ | 210 | /* Copy peer MCS info, the driver might need them. */ |
41 | memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, | 211 | memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, |
42 | sizeof(struct ieee80211_vht_mcs_info)); | 212 | sizeof(struct ieee80211_vht_mcs_info)); |
43 | 213 | ||
214 | /* but also restrict MCSes */ | ||
215 | for (i = 0; i < 8; i++) { | ||
216 | u16 own_rx, own_tx, peer_rx, peer_tx; | ||
217 | |||
218 | own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); | ||
219 | own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
220 | |||
221 | own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); | ||
222 | own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
223 | |||
224 | peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); | ||
225 | peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
226 | |||
227 | peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); | ||
228 | peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
229 | |||
230 | if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { | ||
231 | if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) | ||
232 | peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
233 | else if (own_rx < peer_tx) | ||
234 | peer_tx = own_rx; | ||
235 | } | ||
236 | |||
237 | if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { | ||
238 | if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) | ||
239 | peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; | ||
240 | else if (own_tx < peer_rx) | ||
241 | peer_rx = own_tx; | ||
242 | } | ||
243 | |||
244 | vht_cap->vht_mcs.rx_mcs_map &= | ||
245 | ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); | ||
246 | vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); | ||
247 | |||
248 | vht_cap->vht_mcs.tx_mcs_map &= | ||
249 | ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); | ||
250 | vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); | ||
251 | } | ||
252 | |||
253 | /* finally set up the bandwidth */ | ||
44 | switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { | 254 | switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { |
45 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: | 255 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: |
46 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: | 256 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: |