diff options
Diffstat (limited to 'net/mac80211/agg-tx.c')
-rw-r--r-- | net/mac80211/agg-tx.c | 177 |
1 files changed, 125 insertions, 52 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 7f2042d37904..ee8bfe18b488 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c | |||
@@ -146,6 +146,13 @@ static int ___ieee80211_stop_tx_ba_session( | |||
146 | if (WARN_ON(!tid_tx)) | 146 | if (WARN_ON(!tid_tx)) |
147 | return -ENOENT; | 147 | return -ENOENT; |
148 | 148 | ||
149 | if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { | ||
150 | /* not even started yet! */ | ||
151 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); | ||
152 | call_rcu(&tid_tx->rcu_head, kfree_tid_tx); | ||
153 | return 0; | ||
154 | } | ||
155 | |||
149 | #ifdef CONFIG_MAC80211_HT_DEBUG | 156 | #ifdef CONFIG_MAC80211_HT_DEBUG |
150 | printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n", | 157 | printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n", |
151 | sta->sta.addr, tid); | 158 | sta->sta.addr, tid); |
@@ -255,6 +262,94 @@ ieee80211_wake_queue_agg(struct ieee80211_local *local, int tid) | |||
255 | __release(agg_queue); | 262 | __release(agg_queue); |
256 | } | 263 | } |
257 | 264 | ||
265 | static void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) | ||
266 | { | ||
267 | struct tid_ampdu_tx *tid_tx = sta->ampdu_mlme.tid_tx[tid]; | ||
268 | struct ieee80211_local *local = sta->local; | ||
269 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
270 | u16 start_seq_num; | ||
271 | int ret; | ||
272 | |||
273 | /* | ||
274 | * While we're asking the driver about the aggregation, | ||
275 | * stop the AC queue so that we don't have to worry | ||
276 | * about frames that came in while we were doing that, | ||
277 | * which would require us to put them to the AC pending | ||
278 | * afterwards which just makes the code more complex. | ||
279 | */ | ||
280 | ieee80211_stop_queue_agg(local, tid); | ||
281 | |||
282 | clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); | ||
283 | |||
284 | /* | ||
285 | * This might be off by one due to a race that we can't | ||
286 | * really prevent here without synchronize_net() which | ||
287 | * can't be called now. | ||
288 | */ | ||
289 | start_seq_num = sta->tid_seq[tid] >> 4; | ||
290 | |||
291 | ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, | ||
292 | &sta->sta, tid, &start_seq_num); | ||
293 | if (ret) { | ||
294 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
295 | printk(KERN_DEBUG "BA request denied - HW unavailable for" | ||
296 | " tid %d\n", tid); | ||
297 | #endif | ||
298 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], NULL); | ||
299 | ieee80211_wake_queue_agg(local, tid); | ||
300 | call_rcu(&tid_tx->rcu_head, kfree_tid_tx); | ||
301 | return; | ||
302 | } | ||
303 | |||
304 | /* we can take packets again now */ | ||
305 | ieee80211_wake_queue_agg(local, tid); | ||
306 | |||
307 | /* activate the timer for the recipient's addBA response */ | ||
308 | mod_timer(&tid_tx->addba_resp_timer, jiffies + ADDBA_RESP_INTERVAL); | ||
309 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
310 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | ||
311 | #endif | ||
312 | |||
313 | sta->ampdu_mlme.addba_req_num[tid]++; | ||
314 | |||
315 | /* send AddBA request */ | ||
316 | ieee80211_send_addba_request(sdata, sta->sta.addr, tid, | ||
317 | tid_tx->dialog_token, start_seq_num, | ||
318 | 0x40, 5000); | ||
319 | } | ||
320 | |||
321 | void ieee80211_tx_ba_session_work(struct work_struct *work) | ||
322 | { | ||
323 | struct sta_info *sta = | ||
324 | container_of(work, struct sta_info, ampdu_mlme.work); | ||
325 | struct tid_ampdu_tx *tid_tx; | ||
326 | int tid; | ||
327 | |||
328 | /* | ||
329 | * When this flag is set, new sessions should be | ||
330 | * blocked, and existing sessions will be torn | ||
331 | * down by the code that set the flag, so this | ||
332 | * need not run. | ||
333 | */ | ||
334 | if (test_sta_flags(sta, WLAN_STA_BLOCK_BA)) | ||
335 | return; | ||
336 | |||
337 | spin_lock_bh(&sta->lock); | ||
338 | for (tid = 0; tid < STA_TID_NUM; tid++) { | ||
339 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; | ||
340 | if (!tid_tx) | ||
341 | continue; | ||
342 | |||
343 | if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) | ||
344 | ieee80211_tx_ba_session_handle_start(sta, tid); | ||
345 | else if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, | ||
346 | &tid_tx->state)) | ||
347 | ___ieee80211_stop_tx_ba_session(sta, tid, | ||
348 | WLAN_BACK_INITIATOR); | ||
349 | } | ||
350 | spin_unlock_bh(&sta->lock); | ||
351 | } | ||
352 | |||
258 | int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | 353 | int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) |
259 | { | 354 | { |
260 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | 355 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); |
@@ -262,7 +357,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
262 | struct ieee80211_local *local = sdata->local; | 357 | struct ieee80211_local *local = sdata->local; |
263 | struct tid_ampdu_tx *tid_tx; | 358 | struct tid_ampdu_tx *tid_tx; |
264 | int ret = 0; | 359 | int ret = 0; |
265 | u16 start_seq_num; | ||
266 | 360 | ||
267 | trace_api_start_tx_ba_session(pubsta, tid); | 361 | trace_api_start_tx_ba_session(pubsta, tid); |
268 | 362 | ||
@@ -316,15 +410,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
316 | goto err_unlock_sta; | 410 | goto err_unlock_sta; |
317 | } | 411 | } |
318 | 412 | ||
319 | /* | ||
320 | * While we're asking the driver about the aggregation, | ||
321 | * stop the AC queue so that we don't have to worry | ||
322 | * about frames that came in while we were doing that, | ||
323 | * which would require us to put them to the AC pending | ||
324 | * afterwards which just makes the code more complex. | ||
325 | */ | ||
326 | ieee80211_stop_queue_agg(local, tid); | ||
327 | |||
328 | /* prepare A-MPDU MLME for Tx aggregation */ | 413 | /* prepare A-MPDU MLME for Tx aggregation */ |
329 | tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); | 414 | tid_tx = kzalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); |
330 | if (!tid_tx) { | 415 | if (!tid_tx) { |
@@ -334,59 +419,27 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
334 | tid); | 419 | tid); |
335 | #endif | 420 | #endif |
336 | ret = -ENOMEM; | 421 | ret = -ENOMEM; |
337 | goto err_wake_queue; | 422 | goto err_unlock_sta; |
338 | } | 423 | } |
339 | 424 | ||
340 | skb_queue_head_init(&tid_tx->pending); | 425 | skb_queue_head_init(&tid_tx->pending); |
426 | __set_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); | ||
341 | 427 | ||
342 | /* Tx timer */ | 428 | /* Tx timer */ |
343 | tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; | 429 | tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired; |
344 | tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; | 430 | tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid]; |
345 | init_timer(&tid_tx->addba_resp_timer); | 431 | init_timer(&tid_tx->addba_resp_timer); |
346 | 432 | ||
347 | start_seq_num = sta->tid_seq[tid] >> 4; | 433 | /* assign a dialog token */ |
348 | |||
349 | ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, | ||
350 | pubsta, tid, &start_seq_num); | ||
351 | if (ret) { | ||
352 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
353 | printk(KERN_DEBUG "BA request denied - HW unavailable for" | ||
354 | " tid %d\n", tid); | ||
355 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
356 | goto err_free; | ||
357 | } | ||
358 | |||
359 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); | ||
360 | |||
361 | /* Driver vetoed or OKed, but we can take packets again now */ | ||
362 | ieee80211_wake_queue_agg(local, tid); | ||
363 | |||
364 | /* activate the timer for the recipient's addBA response */ | ||
365 | tid_tx->addba_resp_timer.expires = jiffies + ADDBA_RESP_INTERVAL; | ||
366 | add_timer(&tid_tx->addba_resp_timer); | ||
367 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
368 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | ||
369 | #endif | ||
370 | |||
371 | /* prepare tid data */ | ||
372 | sta->ampdu_mlme.dialog_token_allocator++; | 434 | sta->ampdu_mlme.dialog_token_allocator++; |
373 | tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; | 435 | tid_tx->dialog_token = sta->ampdu_mlme.dialog_token_allocator; |
374 | tid_tx->ssn = start_seq_num; | ||
375 | |||
376 | sta->ampdu_mlme.addba_req_num[tid]++; | ||
377 | 436 | ||
378 | spin_unlock_bh(&sta->lock); | 437 | /* finally, assign it to the array */ |
438 | rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); | ||
379 | 439 | ||
380 | /* send AddBA request */ | 440 | ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); |
381 | ieee80211_send_addba_request(sdata, pubsta->addr, tid, | ||
382 | tid_tx->dialog_token, tid_tx->ssn, | ||
383 | 0x40, 5000); | ||
384 | return 0; | ||
385 | 441 | ||
386 | err_free: | 442 | /* this flow continues off the work */ |
387 | kfree(tid_tx); | ||
388 | err_wake_queue: | ||
389 | ieee80211_wake_queue_agg(local, tid); | ||
390 | err_unlock_sta: | 443 | err_unlock_sta: |
391 | spin_unlock_bh(&sta->lock); | 444 | spin_unlock_bh(&sta->lock); |
392 | return ret; | 445 | return ret; |
@@ -534,8 +587,7 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, | |||
534 | spin_lock_bh(&sta->lock); | 587 | spin_lock_bh(&sta->lock); |
535 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; | 588 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; |
536 | 589 | ||
537 | /* check if the TID is in aggregation */ | 590 | if (!tid_tx) { |
538 | if (!tid_tx || !test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) { | ||
539 | ret = -ENOENT; | 591 | ret = -ENOENT; |
540 | goto unlock; | 592 | goto unlock; |
541 | } | 593 | } |
@@ -552,6 +604,8 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
552 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); | 604 | struct sta_info *sta = container_of(pubsta, struct sta_info, sta); |
553 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 605 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
554 | struct ieee80211_local *local = sdata->local; | 606 | struct ieee80211_local *local = sdata->local; |
607 | struct tid_ampdu_tx *tid_tx; | ||
608 | int ret = 0; | ||
555 | 609 | ||
556 | trace_api_stop_tx_ba_session(pubsta, tid); | 610 | trace_api_stop_tx_ba_session(pubsta, tid); |
557 | 611 | ||
@@ -561,7 +615,26 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) | |||
561 | if (tid >= STA_TID_NUM) | 615 | if (tid >= STA_TID_NUM) |
562 | return -EINVAL; | 616 | return -EINVAL; |
563 | 617 | ||
564 | return __ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR); | 618 | spin_lock_bh(&sta->lock); |
619 | tid_tx = sta->ampdu_mlme.tid_tx[tid]; | ||
620 | |||
621 | if (!tid_tx) { | ||
622 | ret = -ENOENT; | ||
623 | goto unlock; | ||
624 | } | ||
625 | |||
626 | if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { | ||
627 | /* already in progress stopping it */ | ||
628 | ret = 0; | ||
629 | goto unlock; | ||
630 | } | ||
631 | |||
632 | set_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state); | ||
633 | ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); | ||
634 | |||
635 | unlock: | ||
636 | spin_unlock_bh(&sta->lock); | ||
637 | return ret; | ||
565 | } | 638 | } |
566 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); | 639 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); |
567 | 640 | ||