1 /* 2 * VHT handling 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9 #include <linux/ieee80211.h> 10 #include <linux/export.h> 11 #include <net/mac80211.h> 12 #include "ieee80211_i.h" 13 #include "rate.h" 14 15 16 void 17 ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, 18 struct ieee80211_supported_band *sband, 19 const struct ieee80211_vht_cap *vht_cap_ie, 20 struct sta_info *sta) 21 { 22 struct ieee80211_sta_vht_cap *vht_cap = &sta->sta.vht_cap; 23 24 memset(vht_cap, 0, sizeof(*vht_cap)); 25 26 if (!sta->sta.ht_cap.ht_supported) 27 return; 28 29 if (!vht_cap_ie || !sband->vht_cap.vht_supported) 30 return; 31 32 /* A VHT STA must support 40 MHz */ 33 if (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) 34 return; 35 36 vht_cap->vht_supported = true; 37 38 vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info); 39 40 /* Copy peer MCS info, the driver might need them. */ 41 memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, 42 sizeof(struct ieee80211_vht_mcs_info)); 43 44 switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 45 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 46 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 47 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 48 break; 49 default: 50 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 51 } 52 53 sta->sta.bandwidth = ieee80211_sta_cur_vht_bw(sta); 54 } 55 56 enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta) 57 { 58 struct ieee80211_sub_if_data *sdata = sta->sdata; 59 u32 cap = sta->sta.vht_cap.cap; 60 enum ieee80211_sta_rx_bandwidth bw; 61 62 if (!sta->sta.vht_cap.vht_supported) { 63 bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 64 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 65 goto check_max; 66 } 67 68 switch (sdata->vif.bss_conf.chandef.width) { 69 default: 70 WARN_ON_ONCE(1); 71 /* fall through */ 72 case NL80211_CHAN_WIDTH_20_NOHT: 73 case NL80211_CHAN_WIDTH_20: 74 case NL80211_CHAN_WIDTH_40: 75 bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? 76 IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; 77 break; 78 case NL80211_CHAN_WIDTH_160: 79 if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == 80 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ) { 81 bw = IEEE80211_STA_RX_BW_160; 82 break; 83 } 84 /* fall through */ 85 case NL80211_CHAN_WIDTH_80P80: 86 if ((cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) == 87 IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { 88 bw = IEEE80211_STA_RX_BW_160; 89 break; 90 } 91 /* fall through */ 92 case NL80211_CHAN_WIDTH_80: 93 bw = IEEE80211_STA_RX_BW_80; 94 } 95 96 check_max: 97 if (bw > sta->cur_max_bandwidth) 98 bw = sta->cur_max_bandwidth; 99 return bw; 100 } 101 102 void ieee80211_sta_set_rx_nss(struct sta_info *sta) 103 { 104 u8 ht_rx_nss = 0, vht_rx_nss = 0; 105 106 /* if we received a notification already don't overwrite it */ 107 if (sta->sta.rx_nss) 108 return; 109 110 if (sta->sta.ht_cap.ht_supported) { 111 if (sta->sta.ht_cap.mcs.rx_mask[0]) 112 ht_rx_nss++; 113 if (sta->sta.ht_cap.mcs.rx_mask[1]) 114 ht_rx_nss++; 115 if (sta->sta.ht_cap.mcs.rx_mask[2]) 116 ht_rx_nss++; 117 if (sta->sta.ht_cap.mcs.rx_mask[3]) 118 ht_rx_nss++; 119 /* FIXME: consider rx_highest? */ 120 } 121 122 if (sta->sta.vht_cap.vht_supported) { 123 int i; 124 u16 rx_mcs_map; 125 126 rx_mcs_map = le16_to_cpu(sta->sta.vht_cap.vht_mcs.rx_mcs_map); 127 128 for (i = 7; i >= 0; i--) { 129 u8 mcs = (rx_mcs_map >> (2 * i)) & 3; 130 131 if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) { 132 vht_rx_nss = i + 1; 133 break; 134 } 135 } 136 /* FIXME: consider rx_highest? */ 137 } 138 139 ht_rx_nss = max(ht_rx_nss, vht_rx_nss); 140 sta->sta.rx_nss = max_t(u8, 1, ht_rx_nss); 141 } 142 143 void ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata, 144 struct sta_info *sta, u8 opmode, 145 enum ieee80211_band band, bool nss_only) 146 { 147 struct ieee80211_local *local = sdata->local; 148 struct ieee80211_supported_band *sband; 149 enum ieee80211_sta_rx_bandwidth new_bw; 150 u32 changed = 0; 151 u8 nss; 152 153 sband = local->hw.wiphy->bands[band]; 154 155 /* ignore - no support for BF yet */ 156 if (opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF) 157 return; 158 159 nss = opmode & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK; 160 nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; 161 nss += 1; 162 163 if (sta->sta.rx_nss != nss) { 164 sta->sta.rx_nss = nss; 165 changed |= IEEE80211_RC_NSS_CHANGED; 166 } 167 168 if (nss_only) 169 goto change; 170 171 switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { 172 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: 173 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; 174 break; 175 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: 176 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; 177 break; 178 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: 179 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; 180 break; 181 case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: 182 sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; 183 break; 184 } 185 186 new_bw = ieee80211_sta_cur_vht_bw(sta); 187 if (new_bw != sta->sta.bandwidth) { 188 sta->sta.bandwidth = new_bw; 189 changed |= IEEE80211_RC_NSS_CHANGED; 190 } 191 192 change: 193 if (changed) 194 rate_control_rate_update(local, sband, sta, changed); 195 } 196