diff options
Diffstat (limited to 'net/mac80211/ht.c')
-rw-r--r-- | net/mac80211/ht.c | 110 |
1 files changed, 77 insertions, 33 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 61ac7c48ac0c..0db25d4bb223 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -37,6 +37,9 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, | |||
37 | u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); | 37 | u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask); |
38 | int i; | 38 | int i; |
39 | 39 | ||
40 | if (!ht_cap->ht_supported) | ||
41 | return; | ||
42 | |||
40 | if (sdata->vif.type != NL80211_IFTYPE_STATION) { | 43 | if (sdata->vif.type != NL80211_IFTYPE_STATION) { |
41 | /* AP interfaces call this code when adding new stations, | 44 | /* AP interfaces call this code when adding new stations, |
42 | * so just silently ignore non station interfaces. | 45 | * so just silently ignore non station interfaces. |
@@ -89,22 +92,24 @@ void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata, | |||
89 | } | 92 | } |
90 | 93 | ||
91 | 94 | ||
92 | void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | 95 | bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, |
93 | struct ieee80211_supported_band *sband, | 96 | struct ieee80211_supported_band *sband, |
94 | struct ieee80211_ht_cap *ht_cap_ie, | 97 | const struct ieee80211_ht_cap *ht_cap_ie, |
95 | struct ieee80211_sta_ht_cap *ht_cap) | 98 | struct sta_info *sta) |
96 | { | 99 | { |
100 | struct ieee80211_sta_ht_cap ht_cap; | ||
97 | u8 ampdu_info, tx_mcs_set_cap; | 101 | u8 ampdu_info, tx_mcs_set_cap; |
98 | int i, max_tx_streams; | 102 | int i, max_tx_streams; |
103 | bool changed; | ||
104 | enum ieee80211_sta_rx_bandwidth bw; | ||
105 | enum ieee80211_smps_mode smps_mode; | ||
99 | 106 | ||
100 | BUG_ON(!ht_cap); | 107 | memset(&ht_cap, 0, sizeof(ht_cap)); |
101 | |||
102 | memset(ht_cap, 0, sizeof(*ht_cap)); | ||
103 | 108 | ||
104 | if (!ht_cap_ie || !sband->ht_cap.ht_supported) | 109 | if (!ht_cap_ie || !sband->ht_cap.ht_supported) |
105 | return; | 110 | goto apply; |
106 | 111 | ||
107 | ht_cap->ht_supported = true; | 112 | ht_cap.ht_supported = true; |
108 | 113 | ||
109 | /* | 114 | /* |
110 | * The bits listed in this expression should be | 115 | * The bits listed in this expression should be |
@@ -112,7 +117,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | |||
112 | * advertises more then we can't use those thus | 117 | * advertises more then we can't use those thus |
113 | * we mask them out. | 118 | * we mask them out. |
114 | */ | 119 | */ |
115 | ht_cap->cap = le16_to_cpu(ht_cap_ie->cap_info) & | 120 | ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) & |
116 | (sband->ht_cap.cap | | 121 | (sband->ht_cap.cap | |
117 | ~(IEEE80211_HT_CAP_LDPC_CODING | | 122 | ~(IEEE80211_HT_CAP_LDPC_CODING | |
118 | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | | 123 | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | |
@@ -121,44 +126,30 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | |||
121 | IEEE80211_HT_CAP_SGI_40 | | 126 | IEEE80211_HT_CAP_SGI_40 | |
122 | IEEE80211_HT_CAP_DSSSCCK40)); | 127 | IEEE80211_HT_CAP_DSSSCCK40)); |
123 | 128 | ||
124 | /* Unset 40 MHz if we're not using a 40 MHz channel */ | ||
125 | switch (sdata->vif.bss_conf.chandef.width) { | ||
126 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
127 | case NL80211_CHAN_WIDTH_20: | ||
128 | ht_cap->cap &= ~IEEE80211_HT_CAP_SGI_40; | ||
129 | ht_cap->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; | ||
130 | break; | ||
131 | case NL80211_CHAN_WIDTH_40: | ||
132 | case NL80211_CHAN_WIDTH_80: | ||
133 | case NL80211_CHAN_WIDTH_80P80: | ||
134 | case NL80211_CHAN_WIDTH_160: | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | /* | 129 | /* |
139 | * The STBC bits are asymmetric -- if we don't have | 130 | * The STBC bits are asymmetric -- if we don't have |
140 | * TX then mask out the peer's RX and vice versa. | 131 | * TX then mask out the peer's RX and vice versa. |
141 | */ | 132 | */ |
142 | if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) | 133 | if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) |
143 | ht_cap->cap &= ~IEEE80211_HT_CAP_RX_STBC; | 134 | ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC; |
144 | if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) | 135 | if (!(sband->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)) |
145 | ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; | 136 | ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC; |
146 | 137 | ||
147 | ampdu_info = ht_cap_ie->ampdu_params_info; | 138 | ampdu_info = ht_cap_ie->ampdu_params_info; |
148 | ht_cap->ampdu_factor = | 139 | ht_cap.ampdu_factor = |
149 | ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; | 140 | ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR; |
150 | ht_cap->ampdu_density = | 141 | ht_cap.ampdu_density = |
151 | (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; | 142 | (ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2; |
152 | 143 | ||
153 | /* own MCS TX capabilities */ | 144 | /* own MCS TX capabilities */ |
154 | tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; | 145 | tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; |
155 | 146 | ||
156 | /* Copy peer MCS TX capabilities, the driver might need them. */ | 147 | /* Copy peer MCS TX capabilities, the driver might need them. */ |
157 | ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params; | 148 | ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params; |
158 | 149 | ||
159 | /* can we TX with MCS rates? */ | 150 | /* can we TX with MCS rates? */ |
160 | if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) | 151 | if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) |
161 | return; | 152 | goto apply; |
162 | 153 | ||
163 | /* Counting from 0, therefore +1 */ | 154 | /* Counting from 0, therefore +1 */ |
164 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) | 155 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF) |
@@ -176,25 +167,75 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, | |||
176 | * - remainder are multiple spatial streams using unequal modulation | 167 | * - remainder are multiple spatial streams using unequal modulation |
177 | */ | 168 | */ |
178 | for (i = 0; i < max_tx_streams; i++) | 169 | for (i = 0; i < max_tx_streams; i++) |
179 | ht_cap->mcs.rx_mask[i] = | 170 | ht_cap.mcs.rx_mask[i] = |
180 | sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; | 171 | sband->ht_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i]; |
181 | 172 | ||
182 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) | 173 | if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION) |
183 | for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; | 174 | for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE; |
184 | i < IEEE80211_HT_MCS_MASK_LEN; i++) | 175 | i < IEEE80211_HT_MCS_MASK_LEN; i++) |
185 | ht_cap->mcs.rx_mask[i] = | 176 | ht_cap.mcs.rx_mask[i] = |
186 | sband->ht_cap.mcs.rx_mask[i] & | 177 | sband->ht_cap.mcs.rx_mask[i] & |
187 | ht_cap_ie->mcs.rx_mask[i]; | 178 | ht_cap_ie->mcs.rx_mask[i]; |
188 | 179 | ||
189 | /* handle MCS rate 32 too */ | 180 | /* handle MCS rate 32 too */ |
190 | if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) | 181 | if (sband->ht_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) |
191 | ht_cap->mcs.rx_mask[32/8] |= 1; | 182 | ht_cap.mcs.rx_mask[32/8] |= 1; |
192 | 183 | ||
184 | apply: | ||
193 | /* | 185 | /* |
194 | * If user has specified capability over-rides, take care | 186 | * If user has specified capability over-rides, take care |
195 | * of that here. | 187 | * of that here. |
196 | */ | 188 | */ |
197 | ieee80211_apply_htcap_overrides(sdata, ht_cap); | 189 | ieee80211_apply_htcap_overrides(sdata, &ht_cap); |
190 | |||
191 | changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); | ||
192 | |||
193 | memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); | ||
194 | |||
195 | switch (sdata->vif.bss_conf.chandef.width) { | ||
196 | default: | ||
197 | WARN_ON_ONCE(1); | ||
198 | /* fall through */ | ||
199 | case NL80211_CHAN_WIDTH_20_NOHT: | ||
200 | case NL80211_CHAN_WIDTH_20: | ||
201 | bw = IEEE80211_STA_RX_BW_20; | ||
202 | break; | ||
203 | case NL80211_CHAN_WIDTH_40: | ||
204 | case NL80211_CHAN_WIDTH_80: | ||
205 | case NL80211_CHAN_WIDTH_80P80: | ||
206 | case NL80211_CHAN_WIDTH_160: | ||
207 | bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? | ||
208 | IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; | ||
209 | break; | ||
210 | } | ||
211 | |||
212 | if (bw != sta->sta.bandwidth) | ||
213 | changed = true; | ||
214 | sta->sta.bandwidth = bw; | ||
215 | |||
216 | sta->cur_max_bandwidth = | ||
217 | ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ? | ||
218 | IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20; | ||
219 | |||
220 | switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS) | ||
221 | >> IEEE80211_HT_CAP_SM_PS_SHIFT) { | ||
222 | case WLAN_HT_CAP_SM_PS_INVALID: | ||
223 | case WLAN_HT_CAP_SM_PS_STATIC: | ||
224 | smps_mode = IEEE80211_SMPS_STATIC; | ||
225 | break; | ||
226 | case WLAN_HT_CAP_SM_PS_DYNAMIC: | ||
227 | smps_mode = IEEE80211_SMPS_DYNAMIC; | ||
228 | break; | ||
229 | case WLAN_HT_CAP_SM_PS_DISABLED: | ||
230 | smps_mode = IEEE80211_SMPS_OFF; | ||
231 | break; | ||
232 | } | ||
233 | |||
234 | if (smps_mode != sta->sta.smps_mode) | ||
235 | changed = true; | ||
236 | sta->sta.smps_mode = smps_mode; | ||
237 | |||
238 | return changed; | ||
198 | } | 239 | } |
199 | 240 | ||
200 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, | 241 | void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, |
@@ -406,6 +447,9 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, | |||
406 | if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) | 447 | if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) |
407 | smps_mode = IEEE80211_SMPS_AUTOMATIC; | 448 | smps_mode = IEEE80211_SMPS_AUTOMATIC; |
408 | 449 | ||
450 | if (sdata->u.mgd.driver_smps_mode == smps_mode) | ||
451 | return; | ||
452 | |||
409 | sdata->u.mgd.driver_smps_mode = smps_mode; | 453 | sdata->u.mgd.driver_smps_mode = smps_mode; |
410 | 454 | ||
411 | ieee80211_queue_work(&sdata->local->hw, | 455 | ieee80211_queue_work(&sdata->local->hw, |