diff options
Diffstat (limited to 'net/mac80211/status.c')
-rw-r--r-- | net/mac80211/status.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index e51bd2a1a073..d50358c45ab0 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -127,12 +127,32 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local, | |||
127 | dev_kfree_skb(skb); | 127 | dev_kfree_skb(skb); |
128 | } | 128 | } |
129 | 129 | ||
130 | static void ieee80211_check_pending_bar(struct sta_info *sta, u8 *addr, u8 tid) | ||
131 | { | ||
132 | struct tid_ampdu_tx *tid_tx; | ||
133 | |||
134 | tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); | ||
135 | if (!tid_tx || !tid_tx->bar_pending) | ||
136 | return; | ||
137 | |||
138 | tid_tx->bar_pending = false; | ||
139 | ieee80211_send_bar(&sta->sdata->vif, addr, tid, tid_tx->failed_bar_ssn); | ||
140 | } | ||
141 | |||
130 | static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | 142 | static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) |
131 | { | 143 | { |
132 | struct ieee80211_mgmt *mgmt = (void *) skb->data; | 144 | struct ieee80211_mgmt *mgmt = (void *) skb->data; |
133 | struct ieee80211_local *local = sta->local; | 145 | struct ieee80211_local *local = sta->local; |
134 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 146 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
135 | 147 | ||
148 | if (ieee80211_is_data_qos(mgmt->frame_control)) { | ||
149 | struct ieee80211_hdr *hdr = (void *) skb->data; | ||
150 | u8 *qc = ieee80211_get_qos_ctl(hdr); | ||
151 | u16 tid = qc[0] & 0xf; | ||
152 | |||
153 | ieee80211_check_pending_bar(sta, hdr->addr1, tid); | ||
154 | } | ||
155 | |||
136 | if (ieee80211_is_action(mgmt->frame_control) && | 156 | if (ieee80211_is_action(mgmt->frame_control) && |
137 | sdata->vif.type == NL80211_IFTYPE_STATION && | 157 | sdata->vif.type == NL80211_IFTYPE_STATION && |
138 | mgmt->u.action.category == WLAN_CATEGORY_HT && | 158 | mgmt->u.action.category == WLAN_CATEGORY_HT && |
@@ -161,6 +181,18 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) | |||
161 | } | 181 | } |
162 | } | 182 | } |
163 | 183 | ||
184 | static void ieee80211_set_bar_pending(struct sta_info *sta, u8 tid, u16 ssn) | ||
185 | { | ||
186 | struct tid_ampdu_tx *tid_tx; | ||
187 | |||
188 | tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]); | ||
189 | if (!tid_tx) | ||
190 | return; | ||
191 | |||
192 | tid_tx->failed_bar_ssn = ssn; | ||
193 | tid_tx->bar_pending = true; | ||
194 | } | ||
195 | |||
164 | /* | 196 | /* |
165 | * Use a static threshold for now, best value to be determined | 197 | * Use a static threshold for now, best value to be determined |
166 | * by testing ... | 198 | * by testing ... |
@@ -241,23 +273,28 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
241 | tid = qc[0] & 0xf; | 273 | tid = qc[0] & 0xf; |
242 | ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) | 274 | ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) |
243 | & IEEE80211_SCTL_SEQ); | 275 | & IEEE80211_SCTL_SEQ); |
244 | ieee80211_send_bar(sta->sdata, hdr->addr1, | 276 | ieee80211_send_bar(&sta->sdata->vif, hdr->addr1, |
245 | tid, ssn); | 277 | tid, ssn); |
246 | } | 278 | } |
247 | 279 | ||
248 | if (!acked && ieee80211_is_back_req(fc)) { | 280 | if (!acked && ieee80211_is_back_req(fc)) { |
281 | u16 control; | ||
282 | |||
249 | /* | 283 | /* |
250 | * BAR failed, let's tear down the BA session as a | 284 | * BAR failed, store the last SSN and retry sending |
251 | * last resort as some STAs (Intel 5100 on Windows) | 285 | * the BAR when the next unicast transmission on the |
252 | * can get stuck when the BA window isn't flushed | 286 | * same TID succeeds. |
253 | * correctly. | ||
254 | */ | 287 | */ |
255 | bar = (struct ieee80211_bar *) skb->data; | 288 | bar = (struct ieee80211_bar *) skb->data; |
256 | if (!(bar->control & IEEE80211_BAR_CTRL_MULTI_TID)) { | 289 | control = le16_to_cpu(bar->control); |
257 | tid = (bar->control & | 290 | if (!(control & IEEE80211_BAR_CTRL_MULTI_TID)) { |
291 | u16 ssn = le16_to_cpu(bar->start_seq_num); | ||
292 | |||
293 | tid = (control & | ||
258 | IEEE80211_BAR_CTRL_TID_INFO_MASK) >> | 294 | IEEE80211_BAR_CTRL_TID_INFO_MASK) >> |
259 | IEEE80211_BAR_CTRL_TID_INFO_SHIFT; | 295 | IEEE80211_BAR_CTRL_TID_INFO_SHIFT; |
260 | ieee80211_stop_tx_ba_session(&sta->sta, tid); | 296 | |
297 | ieee80211_set_bar_pending(sta, tid, ssn); | ||
261 | } | 298 | } |
262 | } | 299 | } |
263 | 300 | ||