1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * VHT handling |
4 | * |
5 | * Portions of this file |
6 | * Copyright(c) 2015 - 2016 Intel Deutschland GmbH |
7 | * Copyright (C) 2018 - 2023 Intel Corporation |
8 | */ |
9 | |
10 | #include <linux/ieee80211.h> |
11 | #include <linux/export.h> |
12 | #include <net/mac80211.h> |
13 | #include "ieee80211_i.h" |
14 | #include "rate.h" |
15 | |
16 | |
17 | static void __check_vhtcap_disable(struct ieee80211_sub_if_data *sdata, |
18 | struct ieee80211_sta_vht_cap *vht_cap, |
19 | u32 flag) |
20 | { |
21 | __le32 le_flag = cpu_to_le32(flag); |
22 | |
23 | if (sdata->u.mgd.vht_capa_mask.vht_cap_info & le_flag && |
24 | !(sdata->u.mgd.vht_capa.vht_cap_info & le_flag)) |
25 | vht_cap->cap &= ~flag; |
26 | } |
27 | |
28 | void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, |
29 | struct ieee80211_sta_vht_cap *vht_cap) |
30 | { |
31 | int i; |
32 | u16 rxmcs_mask, rxmcs_cap, rxmcs_n, txmcs_mask, txmcs_cap, txmcs_n; |
33 | |
34 | if (!vht_cap->vht_supported) |
35 | return; |
36 | |
37 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
38 | return; |
39 | |
40 | __check_vhtcap_disable(sdata, vht_cap, |
41 | IEEE80211_VHT_CAP_RXLDPC); |
42 | __check_vhtcap_disable(sdata, vht_cap, |
43 | IEEE80211_VHT_CAP_SHORT_GI_80); |
44 | __check_vhtcap_disable(sdata, vht_cap, |
45 | IEEE80211_VHT_CAP_SHORT_GI_160); |
46 | __check_vhtcap_disable(sdata, vht_cap, |
47 | IEEE80211_VHT_CAP_TXSTBC); |
48 | __check_vhtcap_disable(sdata, vht_cap, |
49 | IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); |
50 | __check_vhtcap_disable(sdata, vht_cap, |
51 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); |
52 | __check_vhtcap_disable(sdata, vht_cap, |
53 | IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN); |
54 | __check_vhtcap_disable(sdata, vht_cap, |
55 | IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN); |
56 | |
57 | /* Allow user to decrease AMPDU length exponent */ |
58 | if (sdata->u.mgd.vht_capa_mask.vht_cap_info & |
59 | cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) { |
60 | u32 cap, n; |
61 | |
62 | n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) & |
63 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; |
64 | n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; |
65 | cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; |
66 | cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; |
67 | |
68 | if (n < cap) { |
69 | vht_cap->cap &= |
70 | ~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; |
71 | vht_cap->cap |= |
72 | n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; |
73 | } |
74 | } |
75 | |
76 | /* Allow the user to decrease MCSes */ |
77 | rxmcs_mask = |
78 | le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map); |
79 | rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map); |
80 | rxmcs_n &= rxmcs_mask; |
81 | rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); |
82 | |
83 | txmcs_mask = |
84 | le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map); |
85 | txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map); |
86 | txmcs_n &= txmcs_mask; |
87 | txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); |
88 | for (i = 0; i < 8; i++) { |
89 | u8 m, n, c; |
90 | |
91 | m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
92 | n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
93 | c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
94 | |
95 | if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || |
96 | n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { |
97 | rxmcs_cap &= ~(3 << 2*i); |
98 | rxmcs_cap |= (rxmcs_n & (3 << 2*i)); |
99 | } |
100 | |
101 | m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
102 | n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
103 | c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
104 | |
105 | if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) || |
106 | n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) { |
107 | txmcs_cap &= ~(3 << 2*i); |
108 | txmcs_cap |= (txmcs_n & (3 << 2*i)); |
109 | } |
110 | } |
111 | vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap); |
112 | vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap); |
113 | } |
114 | |
115 | void |
116 | ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, |
117 | struct ieee80211_supported_band *sband, |
118 | const struct ieee80211_vht_cap *vht_cap_ie, |
119 | const struct ieee80211_vht_cap *vht_cap_ie2, |
120 | struct link_sta_info *link_sta) |
121 | { |
122 | struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; |
123 | struct ieee80211_sta_vht_cap own_cap; |
124 | u32 cap_info, i; |
125 | bool have_80mhz; |
126 | u32 mpdu_len; |
127 | |
128 | memset(vht_cap, 0, sizeof(*vht_cap)); |
129 | |
130 | if (!link_sta->pub->ht_cap.ht_supported) |
131 | return; |
132 | |
133 | if (!vht_cap_ie || !sband->vht_cap.vht_supported) |
134 | return; |
135 | |
136 | /* Allow VHT if at least one channel on the sband supports 80 MHz */ |
137 | have_80mhz = false; |
138 | for (i = 0; i < sband->n_channels; i++) { |
139 | if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED | |
140 | IEEE80211_CHAN_NO_80MHZ)) |
141 | continue; |
142 | |
143 | have_80mhz = true; |
144 | break; |
145 | } |
146 | |
147 | if (!have_80mhz) |
148 | return; |
149 | |
150 | /* |
151 | * A VHT STA must support 40 MHz, but if we verify that here |
152 | * then we break a few things - some APs (e.g. Netgear R6300v2 |
153 | * and others based on the BCM4360 chipset) will unset this |
154 | * capability bit when operating in 20 MHz. |
155 | */ |
156 | |
157 | vht_cap->vht_supported = true; |
158 | |
159 | own_cap = sband->vht_cap; |
160 | /* |
161 | * If user has specified capability overrides, take care |
162 | * of that if the station we're setting up is the AP that |
163 | * we advertised a restricted capability set to. Override |
164 | * our own capabilities and then use those below. |
165 | */ |
166 | if (sdata->vif.type == NL80211_IFTYPE_STATION && |
167 | !test_sta_flag(sta: link_sta->sta, flag: WLAN_STA_TDLS_PEER)) |
168 | ieee80211_apply_vhtcap_overrides(sdata, vht_cap: &own_cap); |
169 | |
170 | /* take some capabilities as-is */ |
171 | cap_info = le32_to_cpu(vht_cap_ie->vht_cap_info); |
172 | vht_cap->cap = cap_info; |
173 | vht_cap->cap &= IEEE80211_VHT_CAP_RXLDPC | |
174 | IEEE80211_VHT_CAP_VHT_TXOP_PS | |
175 | IEEE80211_VHT_CAP_HTC_VHT | |
176 | IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | |
177 | IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB | |
178 | IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB | |
179 | IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | |
180 | IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; |
181 | |
182 | vht_cap->cap |= min_t(u32, cap_info & IEEE80211_VHT_CAP_MAX_MPDU_MASK, |
183 | own_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK); |
184 | |
185 | /* and some based on our own capabilities */ |
186 | switch (own_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { |
187 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: |
188 | vht_cap->cap |= cap_info & |
189 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; |
190 | break; |
191 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: |
192 | vht_cap->cap |= cap_info & |
193 | IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
194 | break; |
195 | default: |
196 | /* nothing */ |
197 | break; |
198 | } |
199 | |
200 | /* symmetric capabilities */ |
201 | vht_cap->cap |= cap_info & own_cap.cap & |
202 | (IEEE80211_VHT_CAP_SHORT_GI_80 | |
203 | IEEE80211_VHT_CAP_SHORT_GI_160); |
204 | |
205 | /* remaining ones */ |
206 | if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) |
207 | vht_cap->cap |= cap_info & |
208 | (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | |
209 | IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK); |
210 | |
211 | if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) |
212 | vht_cap->cap |= cap_info & |
213 | (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | |
214 | IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); |
215 | |
216 | if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) |
217 | vht_cap->cap |= cap_info & |
218 | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; |
219 | |
220 | if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) |
221 | vht_cap->cap |= cap_info & |
222 | IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; |
223 | |
224 | if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC) |
225 | vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK; |
226 | |
227 | if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) |
228 | vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC; |
229 | |
230 | /* Copy peer MCS info, the driver might need them. */ |
231 | memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, |
232 | sizeof(struct ieee80211_vht_mcs_info)); |
233 | |
234 | /* copy EXT_NSS_BW Support value or remove the capability */ |
235 | if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_VHT_EXT_NSS_BW)) |
236 | vht_cap->cap |= (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); |
237 | else |
238 | vht_cap->vht_mcs.tx_highest &= |
239 | ~cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); |
240 | |
241 | /* but also restrict MCSes */ |
242 | for (i = 0; i < 8; i++) { |
243 | u16 own_rx, own_tx, peer_rx, peer_tx; |
244 | |
245 | own_rx = le16_to_cpu(own_cap.vht_mcs.rx_mcs_map); |
246 | own_rx = (own_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
247 | |
248 | own_tx = le16_to_cpu(own_cap.vht_mcs.tx_mcs_map); |
249 | own_tx = (own_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
250 | |
251 | peer_rx = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map); |
252 | peer_rx = (peer_rx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
253 | |
254 | peer_tx = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); |
255 | peer_tx = (peer_tx >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
256 | |
257 | if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
258 | if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED) |
259 | peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; |
260 | else if (own_rx < peer_tx) |
261 | peer_tx = own_rx; |
262 | } |
263 | |
264 | if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
265 | if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED) |
266 | peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; |
267 | else if (own_tx < peer_rx) |
268 | peer_rx = own_tx; |
269 | } |
270 | |
271 | vht_cap->vht_mcs.rx_mcs_map &= |
272 | ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); |
273 | vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2); |
274 | |
275 | vht_cap->vht_mcs.tx_mcs_map &= |
276 | ~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2); |
277 | vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2); |
278 | } |
279 | |
280 | /* |
281 | * This is a workaround for VHT-enabled STAs which break the spec |
282 | * and have the VHT-MCS Rx map filled in with value 3 for all eight |
283 | * spacial streams, an example is AR9462. |
284 | * |
285 | * As per spec, in section 22.1.1 Introduction to the VHT PHY |
286 | * A VHT STA shall support at least single spactial stream VHT-MCSs |
287 | * 0 to 7 (transmit and receive) in all supported channel widths. |
288 | */ |
289 | if (vht_cap->vht_mcs.rx_mcs_map == cpu_to_le16(0xFFFF)) { |
290 | vht_cap->vht_supported = false; |
291 | sdata_info(sdata, |
292 | "Ignoring VHT IE from %pM (link:%pM) due to invalid rx_mcs_map\n" , |
293 | link_sta->sta->addr, link_sta->addr); |
294 | return; |
295 | } |
296 | |
297 | /* finally set up the bandwidth */ |
298 | switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { |
299 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: |
300 | case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: |
301 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; |
302 | break; |
303 | default: |
304 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; |
305 | |
306 | if (!(vht_cap->vht_mcs.tx_highest & |
307 | cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) |
308 | break; |
309 | |
310 | /* |
311 | * If this is non-zero, then it does support 160 MHz after all, |
312 | * in one form or the other. We don't distinguish here (or even |
313 | * above) between 160 and 80+80 yet. |
314 | */ |
315 | if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) |
316 | link_sta->cur_max_bandwidth = |
317 | IEEE80211_STA_RX_BW_160; |
318 | } |
319 | |
320 | link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); |
321 | |
322 | /* |
323 | * Work around the Cisco 9115 FW 17.3 bug by taking the min of |
324 | * both reported MPDU lengths. |
325 | */ |
326 | mpdu_len = vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK; |
327 | if (vht_cap_ie2) |
328 | mpdu_len = min_t(u32, mpdu_len, |
329 | le32_get_bits(vht_cap_ie2->vht_cap_info, |
330 | IEEE80211_VHT_CAP_MAX_MPDU_MASK)); |
331 | |
332 | /* |
333 | * FIXME - should the amsdu len be per link? store per link |
334 | * and maintain a minimum? |
335 | */ |
336 | switch (mpdu_len) { |
337 | case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: |
338 | link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; |
339 | break; |
340 | case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991: |
341 | link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; |
342 | break; |
343 | case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: |
344 | default: |
345 | link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; |
346 | break; |
347 | } |
348 | |
349 | ieee80211_sta_recalc_aggregates(pubsta: &link_sta->sta->sta); |
350 | } |
351 | |
352 | /* FIXME: move this to some better location - parses HE/EHT now */ |
353 | enum ieee80211_sta_rx_bandwidth |
354 | ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta) |
355 | { |
356 | unsigned int link_id = link_sta->link_id; |
357 | struct ieee80211_sub_if_data *sdata = link_sta->sta->sdata; |
358 | struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; |
359 | struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; |
360 | struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap; |
361 | u32 cap_width; |
362 | |
363 | if (he_cap->has_he) { |
364 | struct ieee80211_bss_conf *link_conf; |
365 | enum ieee80211_sta_rx_bandwidth ret; |
366 | u8 info; |
367 | |
368 | rcu_read_lock(); |
369 | link_conf = rcu_dereference(sdata->vif.link_conf[link_id]); |
370 | |
371 | if (eht_cap->has_eht && |
372 | link_conf->chandef.chan->band == NL80211_BAND_6GHZ) { |
373 | info = eht_cap->eht_cap_elem.phy_cap_info[0]; |
374 | |
375 | if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) { |
376 | ret = IEEE80211_STA_RX_BW_320; |
377 | goto out; |
378 | } |
379 | } |
380 | |
381 | info = he_cap->he_cap_elem.phy_cap_info[0]; |
382 | |
383 | if (link_conf->chandef.chan->band == NL80211_BAND_2GHZ) { |
384 | if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) |
385 | ret = IEEE80211_STA_RX_BW_40; |
386 | else |
387 | ret = IEEE80211_STA_RX_BW_20; |
388 | goto out; |
389 | } |
390 | |
391 | if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G || |
392 | info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) |
393 | ret = IEEE80211_STA_RX_BW_160; |
394 | else if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) |
395 | ret = IEEE80211_STA_RX_BW_80; |
396 | else |
397 | ret = IEEE80211_STA_RX_BW_20; |
398 | out: |
399 | rcu_read_unlock(); |
400 | |
401 | return ret; |
402 | } |
403 | |
404 | if (!vht_cap->vht_supported) |
405 | return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? |
406 | IEEE80211_STA_RX_BW_40 : |
407 | IEEE80211_STA_RX_BW_20; |
408 | |
409 | cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
410 | |
411 | if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ || |
412 | cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) |
413 | return IEEE80211_STA_RX_BW_160; |
414 | |
415 | /* |
416 | * If this is non-zero, then it does support 160 MHz after all, |
417 | * in one form or the other. We don't distinguish here (or even |
418 | * above) between 160 and 80+80 yet. |
419 | */ |
420 | if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) |
421 | return IEEE80211_STA_RX_BW_160; |
422 | |
423 | return IEEE80211_STA_RX_BW_80; |
424 | } |
425 | |
426 | enum nl80211_chan_width |
427 | ieee80211_sta_cap_chan_bw(struct link_sta_info *link_sta) |
428 | { |
429 | struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; |
430 | u32 cap_width; |
431 | |
432 | if (!vht_cap->vht_supported) { |
433 | if (!link_sta->pub->ht_cap.ht_supported) |
434 | return NL80211_CHAN_WIDTH_20_NOHT; |
435 | |
436 | return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? |
437 | NL80211_CHAN_WIDTH_40 : NL80211_CHAN_WIDTH_20; |
438 | } |
439 | |
440 | cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
441 | |
442 | if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) |
443 | return NL80211_CHAN_WIDTH_160; |
444 | else if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) |
445 | return NL80211_CHAN_WIDTH_80P80; |
446 | |
447 | return NL80211_CHAN_WIDTH_80; |
448 | } |
449 | |
450 | enum nl80211_chan_width |
451 | ieee80211_sta_rx_bw_to_chan_width(struct link_sta_info *link_sta) |
452 | { |
453 | enum ieee80211_sta_rx_bandwidth cur_bw = |
454 | link_sta->pub->bandwidth; |
455 | struct ieee80211_sta_vht_cap *vht_cap = |
456 | &link_sta->pub->vht_cap; |
457 | u32 cap_width; |
458 | |
459 | switch (cur_bw) { |
460 | case IEEE80211_STA_RX_BW_20: |
461 | if (!link_sta->pub->ht_cap.ht_supported) |
462 | return NL80211_CHAN_WIDTH_20_NOHT; |
463 | else |
464 | return NL80211_CHAN_WIDTH_20; |
465 | case IEEE80211_STA_RX_BW_40: |
466 | return NL80211_CHAN_WIDTH_40; |
467 | case IEEE80211_STA_RX_BW_80: |
468 | return NL80211_CHAN_WIDTH_80; |
469 | case IEEE80211_STA_RX_BW_160: |
470 | cap_width = |
471 | vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK; |
472 | |
473 | if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) |
474 | return NL80211_CHAN_WIDTH_160; |
475 | |
476 | return NL80211_CHAN_WIDTH_80P80; |
477 | default: |
478 | return NL80211_CHAN_WIDTH_20; |
479 | } |
480 | } |
481 | |
482 | enum ieee80211_sta_rx_bandwidth |
483 | ieee80211_chan_width_to_rx_bw(enum nl80211_chan_width width) |
484 | { |
485 | switch (width) { |
486 | case NL80211_CHAN_WIDTH_20_NOHT: |
487 | case NL80211_CHAN_WIDTH_20: |
488 | return IEEE80211_STA_RX_BW_20; |
489 | case NL80211_CHAN_WIDTH_40: |
490 | return IEEE80211_STA_RX_BW_40; |
491 | case NL80211_CHAN_WIDTH_80: |
492 | return IEEE80211_STA_RX_BW_80; |
493 | case NL80211_CHAN_WIDTH_160: |
494 | case NL80211_CHAN_WIDTH_80P80: |
495 | return IEEE80211_STA_RX_BW_160; |
496 | case NL80211_CHAN_WIDTH_320: |
497 | return IEEE80211_STA_RX_BW_320; |
498 | default: |
499 | WARN_ON_ONCE(1); |
500 | return IEEE80211_STA_RX_BW_20; |
501 | } |
502 | } |
503 | |
504 | /* FIXME: rename/move - this deals with everything not just VHT */ |
505 | enum ieee80211_sta_rx_bandwidth |
506 | ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta) |
507 | { |
508 | struct sta_info *sta = link_sta->sta; |
509 | struct ieee80211_bss_conf *link_conf; |
510 | enum nl80211_chan_width bss_width; |
511 | enum ieee80211_sta_rx_bandwidth bw; |
512 | |
513 | rcu_read_lock(); |
514 | link_conf = rcu_dereference(sta->sdata->vif.link_conf[link_sta->link_id]); |
515 | if (WARN_ON(!link_conf)) |
516 | bss_width = NL80211_CHAN_WIDTH_20_NOHT; |
517 | else |
518 | bss_width = link_conf->chandef.width; |
519 | rcu_read_unlock(); |
520 | |
521 | bw = ieee80211_sta_cap_rx_bw(link_sta); |
522 | bw = min(bw, link_sta->cur_max_bandwidth); |
523 | |
524 | /* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of |
525 | * IEEE80211-2016 specification makes higher bandwidth operation |
526 | * possible on the TDLS link if the peers have wider bandwidth |
527 | * capability. |
528 | * |
529 | * However, in this case, and only if the TDLS peer is authorized, |
530 | * limit to the tdls_chandef so that the configuration here isn't |
531 | * wider than what's actually requested on the channel context. |
532 | */ |
533 | if (test_sta_flag(sta, flag: WLAN_STA_TDLS_PEER) && |
534 | test_sta_flag(sta, flag: WLAN_STA_TDLS_WIDER_BW) && |
535 | test_sta_flag(sta, flag: WLAN_STA_AUTHORIZED) && |
536 | sta->tdls_chandef.chan) |
537 | bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); |
538 | else |
539 | bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width)); |
540 | |
541 | return bw; |
542 | } |
543 | |
544 | void ieee80211_sta_set_rx_nss(struct link_sta_info *link_sta) |
545 | { |
546 | u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss; |
547 | bool support_160; |
548 | |
549 | /* if we received a notification already don't overwrite it */ |
550 | if (link_sta->pub->rx_nss) |
551 | return; |
552 | |
553 | if (link_sta->pub->eht_cap.has_eht) { |
554 | int i; |
555 | const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp; |
556 | |
557 | /* get the max nss for EHT over all possible bandwidths and mcs */ |
558 | for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++) |
559 | eht_rx_nss = max_t(u8, eht_rx_nss, |
560 | u8_get_bits(rx_nss_mcs[i], |
561 | IEEE80211_EHT_MCS_NSS_RX)); |
562 | } |
563 | |
564 | if (link_sta->pub->he_cap.has_he) { |
565 | int i; |
566 | u8 rx_mcs_80 = 0, rx_mcs_160 = 0; |
567 | const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap; |
568 | u16 mcs_160_map = |
569 | le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); |
570 | u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); |
571 | |
572 | for (i = 7; i >= 0; i--) { |
573 | u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3; |
574 | |
575 | if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) { |
576 | rx_mcs_160 = i + 1; |
577 | break; |
578 | } |
579 | } |
580 | for (i = 7; i >= 0; i--) { |
581 | u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3; |
582 | |
583 | if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) { |
584 | rx_mcs_80 = i + 1; |
585 | break; |
586 | } |
587 | } |
588 | |
589 | support_160 = he_cap->he_cap_elem.phy_cap_info[0] & |
590 | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; |
591 | |
592 | if (support_160) |
593 | he_rx_nss = min(rx_mcs_80, rx_mcs_160); |
594 | else |
595 | he_rx_nss = rx_mcs_80; |
596 | } |
597 | |
598 | if (link_sta->pub->ht_cap.ht_supported) { |
599 | if (link_sta->pub->ht_cap.mcs.rx_mask[0]) |
600 | ht_rx_nss++; |
601 | if (link_sta->pub->ht_cap.mcs.rx_mask[1]) |
602 | ht_rx_nss++; |
603 | if (link_sta->pub->ht_cap.mcs.rx_mask[2]) |
604 | ht_rx_nss++; |
605 | if (link_sta->pub->ht_cap.mcs.rx_mask[3]) |
606 | ht_rx_nss++; |
607 | /* FIXME: consider rx_highest? */ |
608 | } |
609 | |
610 | if (link_sta->pub->vht_cap.vht_supported) { |
611 | int i; |
612 | u16 rx_mcs_map; |
613 | |
614 | rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map); |
615 | |
616 | for (i = 7; i >= 0; i--) { |
617 | u8 mcs = (rx_mcs_map >> (2 * i)) & 3; |
618 | |
619 | if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { |
620 | vht_rx_nss = i + 1; |
621 | break; |
622 | } |
623 | } |
624 | /* FIXME: consider rx_highest? */ |
625 | } |
626 | |
627 | rx_nss = max(vht_rx_nss, ht_rx_nss); |
628 | rx_nss = max(he_rx_nss, rx_nss); |
629 | rx_nss = max(eht_rx_nss, rx_nss); |
630 | link_sta->pub->rx_nss = max_t(u8, 1, rx_nss); |
631 | } |
632 | |
633 | u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, |
634 | struct link_sta_info *link_sta, |
635 | u8 opmode, enum nl80211_band band) |
636 | { |
637 | enum ieee80211_sta_rx_bandwidth new_bw; |
638 | struct sta_opmode_info sta_opmode = {}; |
639 | u32 changed = 0; |
640 | u8 nss, cur_nss; |
641 | |
642 | /* ignore - no support for BF yet */ |
643 | if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) |
644 | return 0; |
645 | |
646 | nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; |
647 | nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; |
648 | nss += 1; |
649 | |
650 | if (link_sta->pub->rx_nss != nss) { |
651 | cur_nss = link_sta->pub->rx_nss; |
652 | /* Reset rx_nss and call ieee80211_sta_set_rx_nss() which |
653 | * will set the same to max nss value calculated based on capability. |
654 | */ |
655 | link_sta->pub->rx_nss = 0; |
656 | ieee80211_sta_set_rx_nss(link_sta); |
657 | /* Do not allow an nss change to rx_nss greater than max_nss |
658 | * negotiated and capped to APs capability during association. |
659 | */ |
660 | if (nss <= link_sta->pub->rx_nss) { |
661 | link_sta->pub->rx_nss = nss; |
662 | sta_opmode.rx_nss = nss; |
663 | changed |= IEEE80211_RC_NSS_CHANGED; |
664 | sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED; |
665 | } else { |
666 | link_sta->pub->rx_nss = cur_nss; |
667 | pr_warn_ratelimited("Ignoring NSS change in VHT Operating Mode Notification from %pM with invalid nss %d" , |
668 | link_sta->pub->addr, nss); |
669 | } |
670 | } |
671 | |
672 | switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { |
673 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: |
674 | /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ |
675 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; |
676 | break; |
677 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: |
678 | /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */ |
679 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; |
680 | break; |
681 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: |
682 | if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80) |
683 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; |
684 | else |
685 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; |
686 | break; |
687 | case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: |
688 | /* legacy only, no longer used by newer spec */ |
689 | link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; |
690 | break; |
691 | } |
692 | |
693 | new_bw = ieee80211_sta_cur_vht_bw(link_sta); |
694 | if (new_bw != link_sta->pub->bandwidth) { |
695 | link_sta->pub->bandwidth = new_bw; |
696 | sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(link_sta); |
697 | changed |= IEEE80211_RC_BW_CHANGED; |
698 | sta_opmode.changed |= STA_OPMODE_MAX_BW_CHANGED; |
699 | } |
700 | |
701 | if (sta_opmode.changed) |
702 | cfg80211_sta_opmode_change_notify(dev: sdata->dev, mac: link_sta->addr, |
703 | sta_opmode: &sta_opmode, GFP_KERNEL); |
704 | |
705 | return changed; |
706 | } |
707 | |
708 | void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata, |
709 | struct ieee80211_link_data *link, |
710 | struct ieee80211_mgmt *mgmt) |
711 | { |
712 | struct ieee80211_bss_conf *link_conf = link->conf; |
713 | |
714 | if (!link_conf->mu_mimo_owner) |
715 | return; |
716 | |
717 | if (!memcmp(p: mgmt->u.action.u.vht_group_notif.position, |
718 | q: link_conf->mu_group.position, WLAN_USER_POSITION_LEN) && |
719 | !memcmp(p: mgmt->u.action.u.vht_group_notif.membership, |
720 | q: link_conf->mu_group.membership, WLAN_MEMBERSHIP_LEN)) |
721 | return; |
722 | |
723 | memcpy(link_conf->mu_group.membership, |
724 | mgmt->u.action.u.vht_group_notif.membership, |
725 | WLAN_MEMBERSHIP_LEN); |
726 | memcpy(link_conf->mu_group.position, |
727 | mgmt->u.action.u.vht_group_notif.position, |
728 | WLAN_USER_POSITION_LEN); |
729 | |
730 | ieee80211_link_info_change_notify(sdata, link, |
731 | changed: BSS_CHANGED_MU_GROUPS); |
732 | } |
733 | |
734 | void ieee80211_update_mu_groups(struct ieee80211_vif *vif, unsigned int link_id, |
735 | const u8 *membership, const u8 *position) |
736 | { |
737 | struct ieee80211_bss_conf *link_conf; |
738 | |
739 | rcu_read_lock(); |
740 | link_conf = rcu_dereference(vif->link_conf[link_id]); |
741 | |
742 | if (!WARN_ON_ONCE(!link_conf || !link_conf->mu_mimo_owner)) { |
743 | memcpy(link_conf->mu_group.membership, membership, |
744 | WLAN_MEMBERSHIP_LEN); |
745 | memcpy(link_conf->mu_group.position, position, |
746 | WLAN_USER_POSITION_LEN); |
747 | } |
748 | rcu_read_unlock(); |
749 | } |
750 | EXPORT_SYMBOL_GPL(ieee80211_update_mu_groups); |
751 | |
752 | void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, |
753 | struct link_sta_info *link_sta, |
754 | u8 opmode, enum nl80211_band band) |
755 | { |
756 | struct ieee80211_local *local = sdata->local; |
757 | struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; |
758 | |
759 | u32 changed = __ieee80211_vht_handle_opmode(sdata, link_sta, |
760 | opmode, band); |
761 | |
762 | if (changed > 0) { |
763 | ieee80211_recalc_min_chandef(sdata, link_id: link_sta->link_id); |
764 | rate_control_rate_update(local, sband, sta: link_sta->sta, |
765 | link_id: link_sta->link_id, changed); |
766 | } |
767 | } |
768 | |
769 | void ieee80211_get_vht_mask_from_cap(__le16 vht_cap, |
770 | u16 vht_mask[NL80211_VHT_NSS_MAX]) |
771 | { |
772 | int i; |
773 | u16 mask, cap = le16_to_cpu(vht_cap); |
774 | |
775 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { |
776 | mask = (cap >> i * 2) & IEEE80211_VHT_MCS_NOT_SUPPORTED; |
777 | switch (mask) { |
778 | case IEEE80211_VHT_MCS_SUPPORT_0_7: |
779 | vht_mask[i] = 0x00FF; |
780 | break; |
781 | case IEEE80211_VHT_MCS_SUPPORT_0_8: |
782 | vht_mask[i] = 0x01FF; |
783 | break; |
784 | case IEEE80211_VHT_MCS_SUPPORT_0_9: |
785 | vht_mask[i] = 0x03FF; |
786 | break; |
787 | case IEEE80211_VHT_MCS_NOT_SUPPORTED: |
788 | default: |
789 | vht_mask[i] = 0; |
790 | break; |
791 | } |
792 | } |
793 | } |
794 | |