diff options
Diffstat (limited to 'net/mac80211/ht.c')
-rw-r--r-- | net/mac80211/ht.c | 377 |
1 files changed, 375 insertions, 2 deletions
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 5ccf1bc17466..c72b3fe3ccdb 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c | |||
@@ -2,8 +2,8 @@ | |||
2 | * HT handling | 2 | * HT handling |
3 | * | 3 | * |
4 | * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> | 4 | * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> |
5 | * Copyright 2004, Instant802 Networks, Inc. | 5 | * Copyright 2002-2005, Instant802 Networks, Inc. |
6 | * Copyright 2005, Devicescape Software, Inc. | 6 | * Copyright 2005-2006, Devicescape Software, Inc. |
7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | 7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> |
8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | 8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> |
9 | * Copyright 2007-2008, Intel Corporation | 9 | * Copyright 2007-2008, Intel Corporation |
@@ -18,6 +18,7 @@ | |||
18 | #include <net/mac80211.h> | 18 | #include <net/mac80211.h> |
19 | #include "ieee80211_i.h" | 19 | #include "ieee80211_i.h" |
20 | #include "sta_info.h" | 20 | #include "sta_info.h" |
21 | #include "wme.h" | ||
21 | 22 | ||
22 | int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, | 23 | int ieee80211_ht_cap_ie_to_ht_info(struct ieee80211_ht_cap *ht_cap_ie, |
23 | struct ieee80211_ht_info *ht_info) | 24 | struct ieee80211_ht_info *ht_info) |
@@ -326,3 +327,375 @@ void ieee80211_sta_tear_down_BA_sessions(struct ieee80211_sub_if_data *sdata, u8 | |||
326 | } | 327 | } |
327 | } | 328 | } |
328 | 329 | ||
330 | int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||
331 | { | ||
332 | struct ieee80211_local *local = hw_to_local(hw); | ||
333 | struct sta_info *sta; | ||
334 | struct ieee80211_sub_if_data *sdata; | ||
335 | u16 start_seq_num; | ||
336 | u8 *state; | ||
337 | int ret; | ||
338 | DECLARE_MAC_BUF(mac); | ||
339 | |||
340 | if (tid >= STA_TID_NUM) | ||
341 | return -EINVAL; | ||
342 | |||
343 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
344 | printk(KERN_DEBUG "Open BA session requested for %s tid %u\n", | ||
345 | print_mac(mac, ra), tid); | ||
346 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
347 | |||
348 | rcu_read_lock(); | ||
349 | |||
350 | sta = sta_info_get(local, ra); | ||
351 | if (!sta) { | ||
352 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
353 | printk(KERN_DEBUG "Could not find the station\n"); | ||
354 | #endif | ||
355 | ret = -ENOENT; | ||
356 | goto exit; | ||
357 | } | ||
358 | |||
359 | spin_lock_bh(&sta->lock); | ||
360 | |||
361 | /* we have tried too many times, receiver does not want A-MPDU */ | ||
362 | if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) { | ||
363 | ret = -EBUSY; | ||
364 | goto err_unlock_sta; | ||
365 | } | ||
366 | |||
367 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
368 | /* check if the TID is not in aggregation flow already */ | ||
369 | if (*state != HT_AGG_STATE_IDLE) { | ||
370 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
371 | printk(KERN_DEBUG "BA request denied - session is not " | ||
372 | "idle on tid %u\n", tid); | ||
373 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
374 | ret = -EAGAIN; | ||
375 | goto err_unlock_sta; | ||
376 | } | ||
377 | |||
378 | /* prepare A-MPDU MLME for Tx aggregation */ | ||
379 | sta->ampdu_mlme.tid_tx[tid] = | ||
380 | kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC); | ||
381 | if (!sta->ampdu_mlme.tid_tx[tid]) { | ||
382 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
383 | if (net_ratelimit()) | ||
384 | printk(KERN_ERR "allocate tx mlme to tid %d failed\n", | ||
385 | tid); | ||
386 | #endif | ||
387 | ret = -ENOMEM; | ||
388 | goto err_unlock_sta; | ||
389 | } | ||
390 | /* Tx timer */ | ||
391 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function = | ||
392 | sta_addba_resp_timer_expired; | ||
393 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.data = | ||
394 | (unsigned long)&sta->timer_to_tid[tid]; | ||
395 | init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); | ||
396 | |||
397 | /* create a new queue for this aggregation */ | ||
398 | ret = ieee80211_ht_agg_queue_add(local, sta, tid); | ||
399 | |||
400 | /* case no queue is available to aggregation | ||
401 | * don't switch to aggregation */ | ||
402 | if (ret) { | ||
403 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
404 | printk(KERN_DEBUG "BA request denied - queue unavailable for" | ||
405 | " tid %d\n", tid); | ||
406 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
407 | goto err_unlock_queue; | ||
408 | } | ||
409 | sdata = sta->sdata; | ||
410 | |||
411 | /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the | ||
412 | * call back right away, it must see that the flow has begun */ | ||
413 | *state |= HT_ADDBA_REQUESTED_MSK; | ||
414 | |||
415 | /* This is slightly racy because the queue isn't stopped */ | ||
416 | start_seq_num = sta->tid_seq[tid]; | ||
417 | |||
418 | if (local->ops->ampdu_action) | ||
419 | ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START, | ||
420 | ra, tid, &start_seq_num); | ||
421 | |||
422 | if (ret) { | ||
423 | /* No need to requeue the packets in the agg queue, since we | ||
424 | * held the tx lock: no packet could be enqueued to the newly | ||
425 | * allocated queue */ | ||
426 | ieee80211_ht_agg_queue_remove(local, sta, tid, 0); | ||
427 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
428 | printk(KERN_DEBUG "BA request denied - HW unavailable for" | ||
429 | " tid %d\n", tid); | ||
430 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
431 | *state = HT_AGG_STATE_IDLE; | ||
432 | goto err_unlock_queue; | ||
433 | } | ||
434 | |||
435 | /* Will put all the packets in the new SW queue */ | ||
436 | ieee80211_requeue(local, ieee802_1d_to_ac[tid]); | ||
437 | spin_unlock_bh(&sta->lock); | ||
438 | |||
439 | /* send an addBA request */ | ||
440 | sta->ampdu_mlme.dialog_token_allocator++; | ||
441 | sta->ampdu_mlme.tid_tx[tid]->dialog_token = | ||
442 | sta->ampdu_mlme.dialog_token_allocator; | ||
443 | sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num; | ||
444 | |||
445 | |||
446 | ieee80211_send_addba_request(sta->sdata, ra, tid, | ||
447 | sta->ampdu_mlme.tid_tx[tid]->dialog_token, | ||
448 | sta->ampdu_mlme.tid_tx[tid]->ssn, | ||
449 | 0x40, 5000); | ||
450 | /* activate the timer for the recipient's addBA response */ | ||
451 | sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.expires = | ||
452 | jiffies + ADDBA_RESP_INTERVAL; | ||
453 | add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer); | ||
454 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
455 | printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid); | ||
456 | #endif | ||
457 | goto exit; | ||
458 | |||
459 | err_unlock_queue: | ||
460 | kfree(sta->ampdu_mlme.tid_tx[tid]); | ||
461 | sta->ampdu_mlme.tid_tx[tid] = NULL; | ||
462 | ret = -EBUSY; | ||
463 | err_unlock_sta: | ||
464 | spin_unlock_bh(&sta->lock); | ||
465 | exit: | ||
466 | rcu_read_unlock(); | ||
467 | return ret; | ||
468 | } | ||
469 | EXPORT_SYMBOL(ieee80211_start_tx_ba_session); | ||
470 | |||
471 | int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, | ||
472 | u8 *ra, u16 tid, | ||
473 | enum ieee80211_back_parties initiator) | ||
474 | { | ||
475 | struct ieee80211_local *local = hw_to_local(hw); | ||
476 | struct sta_info *sta; | ||
477 | u8 *state; | ||
478 | int ret = 0; | ||
479 | DECLARE_MAC_BUF(mac); | ||
480 | |||
481 | if (tid >= STA_TID_NUM) | ||
482 | return -EINVAL; | ||
483 | |||
484 | rcu_read_lock(); | ||
485 | sta = sta_info_get(local, ra); | ||
486 | if (!sta) { | ||
487 | rcu_read_unlock(); | ||
488 | return -ENOENT; | ||
489 | } | ||
490 | |||
491 | /* check if the TID is in aggregation */ | ||
492 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
493 | spin_lock_bh(&sta->lock); | ||
494 | |||
495 | if (*state != HT_AGG_STATE_OPERATIONAL) { | ||
496 | ret = -ENOENT; | ||
497 | goto stop_BA_exit; | ||
498 | } | ||
499 | |||
500 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
501 | printk(KERN_DEBUG "Tx BA session stop requested for %s tid %u\n", | ||
502 | print_mac(mac, ra), tid); | ||
503 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
504 | |||
505 | ieee80211_stop_queue(hw, sta->tid_to_tx_q[tid]); | ||
506 | |||
507 | *state = HT_AGG_STATE_REQ_STOP_BA_MSK | | ||
508 | (initiator << HT_AGG_STATE_INITIATOR_SHIFT); | ||
509 | |||
510 | if (local->ops->ampdu_action) | ||
511 | ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_STOP, | ||
512 | ra, tid, NULL); | ||
513 | |||
514 | /* case HW denied going back to legacy */ | ||
515 | if (ret) { | ||
516 | WARN_ON(ret != -EBUSY); | ||
517 | *state = HT_AGG_STATE_OPERATIONAL; | ||
518 | ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); | ||
519 | goto stop_BA_exit; | ||
520 | } | ||
521 | |||
522 | stop_BA_exit: | ||
523 | spin_unlock_bh(&sta->lock); | ||
524 | rcu_read_unlock(); | ||
525 | return ret; | ||
526 | } | ||
527 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); | ||
528 | |||
529 | void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) | ||
530 | { | ||
531 | struct ieee80211_local *local = hw_to_local(hw); | ||
532 | struct sta_info *sta; | ||
533 | u8 *state; | ||
534 | DECLARE_MAC_BUF(mac); | ||
535 | |||
536 | if (tid >= STA_TID_NUM) { | ||
537 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
538 | printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", | ||
539 | tid, STA_TID_NUM); | ||
540 | #endif | ||
541 | return; | ||
542 | } | ||
543 | |||
544 | rcu_read_lock(); | ||
545 | sta = sta_info_get(local, ra); | ||
546 | if (!sta) { | ||
547 | rcu_read_unlock(); | ||
548 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
549 | printk(KERN_DEBUG "Could not find station: %s\n", | ||
550 | print_mac(mac, ra)); | ||
551 | #endif | ||
552 | return; | ||
553 | } | ||
554 | |||
555 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
556 | spin_lock_bh(&sta->lock); | ||
557 | |||
558 | if (!(*state & HT_ADDBA_REQUESTED_MSK)) { | ||
559 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
560 | printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", | ||
561 | *state); | ||
562 | #endif | ||
563 | spin_unlock_bh(&sta->lock); | ||
564 | rcu_read_unlock(); | ||
565 | return; | ||
566 | } | ||
567 | |||
568 | WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK); | ||
569 | |||
570 | *state |= HT_ADDBA_DRV_READY_MSK; | ||
571 | |||
572 | if (*state == HT_AGG_STATE_OPERATIONAL) { | ||
573 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
574 | printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid); | ||
575 | #endif | ||
576 | ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); | ||
577 | } | ||
578 | spin_unlock_bh(&sta->lock); | ||
579 | rcu_read_unlock(); | ||
580 | } | ||
581 | EXPORT_SYMBOL(ieee80211_start_tx_ba_cb); | ||
582 | |||
583 | void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) | ||
584 | { | ||
585 | struct ieee80211_local *local = hw_to_local(hw); | ||
586 | struct sta_info *sta; | ||
587 | u8 *state; | ||
588 | int agg_queue; | ||
589 | DECLARE_MAC_BUF(mac); | ||
590 | |||
591 | if (tid >= STA_TID_NUM) { | ||
592 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
593 | printk(KERN_DEBUG "Bad TID value: tid = %d (>= %d)\n", | ||
594 | tid, STA_TID_NUM); | ||
595 | #endif | ||
596 | return; | ||
597 | } | ||
598 | |||
599 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
600 | printk(KERN_DEBUG "Stopping Tx BA session for %s tid %d\n", | ||
601 | print_mac(mac, ra), tid); | ||
602 | #endif /* CONFIG_MAC80211_HT_DEBUG */ | ||
603 | |||
604 | rcu_read_lock(); | ||
605 | sta = sta_info_get(local, ra); | ||
606 | if (!sta) { | ||
607 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
608 | printk(KERN_DEBUG "Could not find station: %s\n", | ||
609 | print_mac(mac, ra)); | ||
610 | #endif | ||
611 | rcu_read_unlock(); | ||
612 | return; | ||
613 | } | ||
614 | state = &sta->ampdu_mlme.tid_state_tx[tid]; | ||
615 | |||
616 | /* NOTE: no need to use sta->lock in this state check, as | ||
617 | * ieee80211_stop_tx_ba_session will let only one stop call to | ||
618 | * pass through per sta/tid | ||
619 | */ | ||
620 | if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { | ||
621 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
622 | printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); | ||
623 | #endif | ||
624 | rcu_read_unlock(); | ||
625 | return; | ||
626 | } | ||
627 | |||
628 | if (*state & HT_AGG_STATE_INITIATOR_MSK) | ||
629 | ieee80211_send_delba(sta->sdata, ra, tid, | ||
630 | WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); | ||
631 | |||
632 | agg_queue = sta->tid_to_tx_q[tid]; | ||
633 | |||
634 | ieee80211_ht_agg_queue_remove(local, sta, tid, 1); | ||
635 | |||
636 | /* We just requeued the all the frames that were in the | ||
637 | * removed queue, and since we might miss a softirq we do | ||
638 | * netif_schedule_queue. ieee80211_wake_queue is not used | ||
639 | * here as this queue is not necessarily stopped | ||
640 | */ | ||
641 | netif_schedule_queue(netdev_get_tx_queue(local->mdev, agg_queue)); | ||
642 | spin_lock_bh(&sta->lock); | ||
643 | *state = HT_AGG_STATE_IDLE; | ||
644 | sta->ampdu_mlme.addba_req_num[tid] = 0; | ||
645 | kfree(sta->ampdu_mlme.tid_tx[tid]); | ||
646 | sta->ampdu_mlme.tid_tx[tid] = NULL; | ||
647 | spin_unlock_bh(&sta->lock); | ||
648 | |||
649 | rcu_read_unlock(); | ||
650 | } | ||
651 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb); | ||
652 | |||
653 | void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, | ||
654 | const u8 *ra, u16 tid) | ||
655 | { | ||
656 | struct ieee80211_local *local = hw_to_local(hw); | ||
657 | struct ieee80211_ra_tid *ra_tid; | ||
658 | struct sk_buff *skb = dev_alloc_skb(0); | ||
659 | |||
660 | if (unlikely(!skb)) { | ||
661 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
662 | if (net_ratelimit()) | ||
663 | printk(KERN_WARNING "%s: Not enough memory, " | ||
664 | "dropping start BA session", skb->dev->name); | ||
665 | #endif | ||
666 | return; | ||
667 | } | ||
668 | ra_tid = (struct ieee80211_ra_tid *) &skb->cb; | ||
669 | memcpy(&ra_tid->ra, ra, ETH_ALEN); | ||
670 | ra_tid->tid = tid; | ||
671 | |||
672 | skb->pkt_type = IEEE80211_ADDBA_MSG; | ||
673 | skb_queue_tail(&local->skb_queue, skb); | ||
674 | tasklet_schedule(&local->tasklet); | ||
675 | } | ||
676 | EXPORT_SYMBOL(ieee80211_start_tx_ba_cb_irqsafe); | ||
677 | |||
678 | void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw, | ||
679 | const u8 *ra, u16 tid) | ||
680 | { | ||
681 | struct ieee80211_local *local = hw_to_local(hw); | ||
682 | struct ieee80211_ra_tid *ra_tid; | ||
683 | struct sk_buff *skb = dev_alloc_skb(0); | ||
684 | |||
685 | if (unlikely(!skb)) { | ||
686 | #ifdef CONFIG_MAC80211_HT_DEBUG | ||
687 | if (net_ratelimit()) | ||
688 | printk(KERN_WARNING "%s: Not enough memory, " | ||
689 | "dropping stop BA session", skb->dev->name); | ||
690 | #endif | ||
691 | return; | ||
692 | } | ||
693 | ra_tid = (struct ieee80211_ra_tid *) &skb->cb; | ||
694 | memcpy(&ra_tid->ra, ra, ETH_ALEN); | ||
695 | ra_tid->tid = tid; | ||
696 | |||
697 | skb->pkt_type = IEEE80211_DELBA_MSG; | ||
698 | skb_queue_tail(&local->skb_queue, skb); | ||
699 | tasklet_schedule(&local->tasklet); | ||
700 | } | ||
701 | EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb_irqsafe); | ||