diff options
author | Seth Forshee <seth.forshee@canonical.com> | 2012-11-15 09:07:51 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-11-20 14:03:06 -0500 |
commit | ef2c0512bbf43440a78a6471059e19641eab1e83 (patch) | |
tree | d1df3c2350070ff3a62ba49c65e799f40e551fbf /drivers/net/wireless | |
parent | cb675f5ff39e12836591246e730e11ce648048d2 (diff) |
brcmsmac: Introduce AMPDU sessions for assembling AMPDUs
AMPDU session allows MPDUs to be temporarily queued until either a full
AMPDU has been collected or circumstances dictate that transmission
should start with a partial AMPDU. Packets are added to the session by
calling brcms_c_ampdu_add_frame(). brcms_c_ampdu_finalize() should be
called to fix up the tx headers in the first and last packet before
adding the packets to the DMA ring. brmcs_c_sendampdu() is converted to
using AMPDU sessions.
This patch has no real value on it's own, but is needed in preparation
for elimination of the tx packet queue from brcmsmac.
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Arend van Spriel <arend@broadcom.com>
Tested-by: Daniel Wagner <wagi@monom.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | 625 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmsmac/ampdu.h | 26 |
2 files changed, 377 insertions, 274 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c index be5bcfb9153b..573953453055 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | |||
@@ -498,38 +498,341 @@ brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, | |||
498 | scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; | 498 | scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; |
499 | } | 499 | } |
500 | 500 | ||
501 | void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, | ||
502 | struct brcms_c_info *wlc) | ||
503 | { | ||
504 | session->wlc = wlc; | ||
505 | skb_queue_head_init(&session->skb_list); | ||
506 | session->max_ampdu_len = 0; /* determined from first MPDU */ | ||
507 | session->max_ampdu_frames = 0; /* determined from first MPDU */ | ||
508 | session->ampdu_len = 0; | ||
509 | session->dma_len = 0; | ||
510 | } | ||
511 | |||
512 | /* | ||
513 | * Preps the given packet for AMPDU based on the session data. If the | ||
514 | * frame cannot be accomodated in the current session, -ENOSPC is | ||
515 | * returned. | ||
516 | */ | ||
517 | int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, | ||
518 | struct sk_buff *p) | ||
519 | { | ||
520 | struct brcms_c_info *wlc = session->wlc; | ||
521 | struct ampdu_info *ampdu = wlc->ampdu; | ||
522 | struct scb *scb = &wlc->pri_scb; | ||
523 | struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; | ||
524 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); | ||
525 | struct ieee80211_tx_rate *txrate = tx_info->status.rates; | ||
526 | struct d11txh *txh = (struct d11txh *)p->data; | ||
527 | unsigned ampdu_frames; | ||
528 | u8 ndelim, tid; | ||
529 | u8 *plcp; | ||
530 | uint len; | ||
531 | u16 mcl; | ||
532 | bool fbr_iscck; | ||
533 | bool rr; | ||
534 | |||
535 | ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; | ||
536 | plcp = (u8 *)(txh + 1); | ||
537 | fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); | ||
538 | len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : | ||
539 | BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); | ||
540 | len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN; | ||
541 | |||
542 | ampdu_frames = skb_queue_len(&session->skb_list); | ||
543 | if (ampdu_frames != 0) { | ||
544 | struct sk_buff *first; | ||
545 | |||
546 | if (ampdu_frames + 1 > session->max_ampdu_frames || | ||
547 | session->ampdu_len + len > session->max_ampdu_len) | ||
548 | return -ENOSPC; | ||
549 | |||
550 | /* | ||
551 | * We aren't really out of space if the new frame is of | ||
552 | * a different priority, but we want the same behaviour | ||
553 | * so return -ENOSPC anyway. | ||
554 | * | ||
555 | * XXX: The old AMPDU code did this, but is it really | ||
556 | * necessary? | ||
557 | */ | ||
558 | first = skb_peek(&session->skb_list); | ||
559 | if (p->priority != first->priority) | ||
560 | return -ENOSPC; | ||
561 | } | ||
562 | |||
563 | /* | ||
564 | * Now that we're sure this frame can be accomodated, update the | ||
565 | * session information. | ||
566 | */ | ||
567 | session->ampdu_len += len; | ||
568 | session->dma_len += p->len; | ||
569 | |||
570 | tid = (u8)p->priority; | ||
571 | |||
572 | /* Handle retry limits */ | ||
573 | if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) { | ||
574 | txrate[0].count++; | ||
575 | rr = true; | ||
576 | } else { | ||
577 | txrate[1].count++; | ||
578 | rr = false; | ||
579 | } | ||
580 | |||
581 | if (ampdu_frames == 0) { | ||
582 | u8 plcp0, plcp3, is40, sgi, mcs; | ||
583 | uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; | ||
584 | struct brcms_fifo_info *f = &du->fifo_tb[fifo]; | ||
585 | |||
586 | if (rr) { | ||
587 | plcp0 = plcp[0]; | ||
588 | plcp3 = plcp[3]; | ||
589 | } else { | ||
590 | plcp0 = txh->FragPLCPFallback[0]; | ||
591 | plcp3 = txh->FragPLCPFallback[3]; | ||
592 | |||
593 | } | ||
594 | |||
595 | /* Limit AMPDU size based on MCS */ | ||
596 | is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; | ||
597 | sgi = plcp3_issgi(plcp3) ? 1 : 0; | ||
598 | mcs = plcp0 & ~MIMO_PLCP_40MHZ; | ||
599 | session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes, | ||
600 | ampdu->max_txlen[mcs][is40][sgi]); | ||
601 | |||
602 | session->max_ampdu_frames = scb_ampdu->max_pdu; | ||
603 | if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { | ||
604 | session->max_ampdu_frames = | ||
605 | min_t(u16, f->mcs2ampdu_table[mcs], | ||
606 | session->max_ampdu_frames); | ||
607 | } | ||
608 | } | ||
609 | |||
610 | /* | ||
611 | * Treat all frames as "middle" frames of AMPDU here. First and | ||
612 | * last frames must be fixed up after all MPDUs have been prepped. | ||
613 | */ | ||
614 | mcl = le16_to_cpu(txh->MacTxControlLow); | ||
615 | mcl &= ~TXC_AMPDU_MASK; | ||
616 | mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); | ||
617 | mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); | ||
618 | txh->MacTxControlLow = cpu_to_le16(mcl); | ||
619 | txh->PreloadSize = 0; /* always default to 0 */ | ||
620 | |||
621 | skb_queue_tail(&session->skb_list, p); | ||
622 | |||
623 | return 0; | ||
624 | } | ||
625 | |||
626 | void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session) | ||
627 | { | ||
628 | struct brcms_c_info *wlc = session->wlc; | ||
629 | struct ampdu_info *ampdu = wlc->ampdu; | ||
630 | struct sk_buff *first, *last; | ||
631 | struct d11txh *txh; | ||
632 | struct ieee80211_tx_info *tx_info; | ||
633 | struct ieee80211_tx_rate *txrate; | ||
634 | u8 ndelim; | ||
635 | u8 *plcp; | ||
636 | uint len; | ||
637 | uint fifo; | ||
638 | struct brcms_fifo_info *f; | ||
639 | u16 mcl; | ||
640 | bool fbr; | ||
641 | bool fbr_iscck; | ||
642 | struct ieee80211_rts *rts; | ||
643 | bool use_rts = false, use_cts = false; | ||
644 | u16 dma_len = session->dma_len; | ||
645 | u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; | ||
646 | u32 rspec = 0, rspec_fallback = 0; | ||
647 | u32 rts_rspec = 0, rts_rspec_fallback = 0; | ||
648 | u8 plcp0, plcp3, is40, sgi, mcs; | ||
649 | u16 mch; | ||
650 | u8 preamble_type = BRCMS_GF_PREAMBLE; | ||
651 | u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; | ||
652 | u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; | ||
653 | u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; | ||
654 | |||
655 | if (skb_queue_empty(&session->skb_list)) | ||
656 | return; | ||
657 | |||
658 | first = skb_peek(&session->skb_list); | ||
659 | last = skb_peek_tail(&session->skb_list); | ||
660 | |||
661 | /* Need to fix up last MPDU first to adjust AMPDU length */ | ||
662 | txh = (struct d11txh *)last->data; | ||
663 | fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; | ||
664 | f = &du->fifo_tb[fifo]; | ||
665 | |||
666 | mcl = le16_to_cpu(txh->MacTxControlLow); | ||
667 | mcl &= ~TXC_AMPDU_MASK; | ||
668 | mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); | ||
669 | txh->MacTxControlLow = cpu_to_le16(mcl); | ||
670 | |||
671 | /* remove the null delimiter after last mpdu */ | ||
672 | ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; | ||
673 | txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; | ||
674 | session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; | ||
675 | |||
676 | /* remove the pad len from last mpdu */ | ||
677 | fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); | ||
678 | len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : | ||
679 | BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); | ||
680 | session->ampdu_len -= roundup(len, 4) - len; | ||
681 | |||
682 | /* Now fix up the first MPDU */ | ||
683 | tx_info = IEEE80211_SKB_CB(first); | ||
684 | txrate = tx_info->status.rates; | ||
685 | txh = (struct d11txh *)first->data; | ||
686 | plcp = (u8 *)(txh + 1); | ||
687 | rts = (struct ieee80211_rts *)&txh->rts_frame; | ||
688 | |||
689 | mcl = le16_to_cpu(txh->MacTxControlLow); | ||
690 | /* If only one MPDU leave it marked as last */ | ||
691 | if (first != last) { | ||
692 | mcl &= ~TXC_AMPDU_MASK; | ||
693 | mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); | ||
694 | } | ||
695 | mcl |= TXC_STARTMSDU; | ||
696 | if (ieee80211_is_rts(rts->frame_control)) { | ||
697 | mcl |= TXC_SENDRTS; | ||
698 | use_rts = true; | ||
699 | } | ||
700 | if (ieee80211_is_cts(rts->frame_control)) { | ||
701 | mcl |= TXC_SENDCTS; | ||
702 | use_cts = true; | ||
703 | } | ||
704 | txh->MacTxControlLow = cpu_to_le16(mcl); | ||
705 | |||
706 | fbr = txrate[1].count > 0; | ||
707 | if (!fbr) { | ||
708 | plcp0 = plcp[0]; | ||
709 | plcp3 = plcp[3]; | ||
710 | } else { | ||
711 | plcp0 = txh->FragPLCPFallback[0]; | ||
712 | plcp3 = txh->FragPLCPFallback[3]; | ||
713 | } | ||
714 | is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; | ||
715 | sgi = plcp3_issgi(plcp3) ? 1 : 0; | ||
716 | mcs = plcp0 & ~MIMO_PLCP_40MHZ; | ||
717 | |||
718 | if (is40) { | ||
719 | if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi))) | ||
720 | mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP; | ||
721 | else | ||
722 | mimo_ctlchbw = PHY_TXC1_BW_20MHZ; | ||
723 | } | ||
724 | |||
725 | /* rebuild the rspec and rspec_fallback */ | ||
726 | rspec = RSPEC_MIMORATE; | ||
727 | rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; | ||
728 | if (plcp[0] & MIMO_PLCP_40MHZ) | ||
729 | rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); | ||
730 | |||
731 | fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); | ||
732 | if (fbr_iscck) { | ||
733 | rspec_fallback = | ||
734 | cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0])); | ||
735 | } else { | ||
736 | rspec_fallback = RSPEC_MIMORATE; | ||
737 | rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; | ||
738 | if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) | ||
739 | rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT; | ||
740 | } | ||
741 | |||
742 | if (use_rts || use_cts) { | ||
743 | rts_rspec = | ||
744 | brcms_c_rspec_to_rts_rspec(wlc, rspec, | ||
745 | false, mimo_ctlchbw); | ||
746 | rts_rspec_fallback = | ||
747 | brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback, | ||
748 | false, mimo_ctlchbw); | ||
749 | } | ||
750 | |||
751 | BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len); | ||
752 | /* mark plcp to indicate ampdu */ | ||
753 | BRCMS_SET_MIMO_PLCP_AMPDU(plcp); | ||
754 | |||
755 | /* reset the mixed mode header durations */ | ||
756 | if (txh->MModeLen) { | ||
757 | u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec, | ||
758 | session->ampdu_len); | ||
759 | txh->MModeLen = cpu_to_le16(mmodelen); | ||
760 | preamble_type = BRCMS_MM_PREAMBLE; | ||
761 | } | ||
762 | if (txh->MModeFbrLen) { | ||
763 | u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback, | ||
764 | session->ampdu_len); | ||
765 | txh->MModeFbrLen = cpu_to_le16(mmfbrlen); | ||
766 | fbr_preamble_type = BRCMS_MM_PREAMBLE; | ||
767 | } | ||
768 | |||
769 | /* set the preload length */ | ||
770 | if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { | ||
771 | dma_len = min(dma_len, f->ampdu_pld_size); | ||
772 | txh->PreloadSize = cpu_to_le16(dma_len); | ||
773 | } else { | ||
774 | txh->PreloadSize = 0; | ||
775 | } | ||
776 | |||
777 | mch = le16_to_cpu(txh->MacTxControlHigh); | ||
778 | |||
779 | /* update RTS dur fields */ | ||
780 | if (use_rts || use_cts) { | ||
781 | u16 durid; | ||
782 | if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == | ||
783 | TXC_PREAMBLE_RTS_MAIN_SHORT) | ||
784 | rts_preamble_type = BRCMS_SHORT_PREAMBLE; | ||
785 | |||
786 | if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == | ||
787 | TXC_PREAMBLE_RTS_FB_SHORT) | ||
788 | rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; | ||
789 | |||
790 | durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, | ||
791 | rspec, rts_preamble_type, | ||
792 | preamble_type, | ||
793 | session->ampdu_len, true); | ||
794 | rts->duration = cpu_to_le16(durid); | ||
795 | durid = brcms_c_compute_rtscts_dur(wlc, use_cts, | ||
796 | rts_rspec_fallback, | ||
797 | rspec_fallback, | ||
798 | rts_fbr_preamble_type, | ||
799 | fbr_preamble_type, | ||
800 | session->ampdu_len, true); | ||
801 | txh->RTSDurFallback = cpu_to_le16(durid); | ||
802 | /* set TxFesTimeNormal */ | ||
803 | txh->TxFesTimeNormal = rts->duration; | ||
804 | /* set fallback rate version of TxFesTimeNormal */ | ||
805 | txh->TxFesTimeFallback = txh->RTSDurFallback; | ||
806 | } | ||
807 | |||
808 | /* set flag and plcp for fallback rate */ | ||
809 | if (fbr) { | ||
810 | mch |= TXC_AMPDU_FBR; | ||
811 | txh->MacTxControlHigh = cpu_to_le16(mch); | ||
812 | BRCMS_SET_MIMO_PLCP_AMPDU(plcp); | ||
813 | BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); | ||
814 | } | ||
815 | |||
816 | BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n", | ||
817 | wlc->pub->unit, skb_queue_len(&session->skb_list), | ||
818 | session->ampdu_len); | ||
819 | } | ||
820 | |||
501 | int | 821 | int |
502 | brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, | 822 | brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, |
503 | struct sk_buff **pdu, int prec) | 823 | struct sk_buff **pdu, int prec) |
504 | { | 824 | { |
505 | struct brcms_c_info *wlc; | 825 | struct brcms_c_info *wlc; |
506 | struct sk_buff *p, *pkt[AMPDU_MAX_MPDU]; | 826 | struct sk_buff *p; |
507 | u8 tid, ndelim; | 827 | struct brcms_ampdu_session session; |
508 | int err = 0; | 828 | int err = 0; |
509 | u8 preamble_type = BRCMS_GF_PREAMBLE; | 829 | u8 tid; |
510 | u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; | ||
511 | u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; | ||
512 | u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; | ||
513 | 830 | ||
514 | bool rr = true, fbr = false; | 831 | uint count, fifo, seg_cnt = 0; |
515 | uint i, count = 0, fifo, seg_cnt = 0; | ||
516 | u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0; | ||
517 | u32 ampdu_len, max_ampdu_bytes = 0; | ||
518 | struct d11txh *txh = NULL; | ||
519 | u8 *plcp; | ||
520 | struct ieee80211_hdr *h; | ||
521 | struct scb *scb; | 832 | struct scb *scb; |
522 | struct scb_ampdu *scb_ampdu; | 833 | struct scb_ampdu *scb_ampdu; |
523 | struct scb_ampdu_tid_ini *ini; | 834 | struct scb_ampdu_tid_ini *ini; |
524 | u8 mcs = 0; | ||
525 | bool use_rts = false, use_cts = false; | ||
526 | u32 rspec = 0, rspec_fallback = 0; | ||
527 | u32 rts_rspec = 0, rts_rspec_fallback = 0; | ||
528 | u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; | ||
529 | struct ieee80211_rts *rts; | ||
530 | u8 rr_retry_limit; | ||
531 | struct brcms_fifo_info *f; | 835 | struct brcms_fifo_info *f; |
532 | bool fbr_iscck; | ||
533 | struct ieee80211_tx_info *tx_info; | 836 | struct ieee80211_tx_info *tx_info; |
534 | u16 qlen; | 837 | u16 qlen; |
535 | struct wiphy *wiphy; | 838 | struct wiphy *wiphy; |
@@ -554,9 +857,8 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, | |||
554 | return -EBUSY; | 857 | return -EBUSY; |
555 | 858 | ||
556 | /* at this point we intend to transmit an AMPDU */ | 859 | /* at this point we intend to transmit an AMPDU */ |
557 | rr_retry_limit = ampdu->rr_retry_limit_tid[tid]; | 860 | brcms_c_ampdu_reset_session(&session, wlc); |
558 | ampdu_len = 0; | 861 | |
559 | dma_len = 0; | ||
560 | while (p) { | 862 | while (p) { |
561 | struct ieee80211_tx_rate *txrate; | 863 | struct ieee80211_tx_rate *txrate; |
562 | 864 | ||
@@ -575,157 +877,35 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, | |||
575 | if (err) { | 877 | if (err) { |
576 | if (err == -EBUSY) { | 878 | if (err == -EBUSY) { |
577 | wiphy_err(wiphy, "wl%d: sendampdu: " | 879 | wiphy_err(wiphy, "wl%d: sendampdu: " |
578 | "prep_xdu retry; seq 0x%x\n", | 880 | "prep_xdu retry\n", wlc->pub->unit); |
579 | wlc->pub->unit, seq); | ||
580 | *pdu = p; | 881 | *pdu = p; |
581 | break; | 882 | break; |
582 | } | 883 | } |
583 | 884 | ||
584 | /* error in the packet; reject it */ | 885 | /* error in the packet; reject it */ |
585 | wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu " | 886 | wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu " |
586 | "rejected; seq 0x%x\n", wlc->pub->unit, seq); | 887 | "rejected\n", wlc->pub->unit); |
587 | *pdu = NULL; | 888 | *pdu = NULL; |
588 | break; | 889 | break; |
589 | } | 890 | } |
590 | 891 | ||
591 | /* pkt is good to be aggregated */ | 892 | err = brcms_c_ampdu_add_frame(&session, p); |
592 | txh = (struct d11txh *) p->data; | 893 | if (err == -ENOSPC) { |
593 | plcp = (u8 *) (txh + 1); | 894 | /* |
594 | h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); | 895 | * No space for this packet in the AMPDU. |
595 | seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; | 896 | * Requeue packet and proceed; |
596 | index = TX_SEQ_TO_INDEX(seq); | 897 | */ |
597 | 898 | *pdu = p; | |
598 | /* check mcl fields and test whether it can be agg'd */ | 899 | break; |
599 | mcl = le16_to_cpu(txh->MacTxControlLow); | 900 | } else if (err) { |
600 | mcl &= ~TXC_AMPDU_MASK; | 901 | /* Unexpected error; reject packet */ |
601 | fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3); | 902 | wiphy_err(wiphy, "wl%d: sendampdu: add_frame rejected", |
602 | txh->PreloadSize = 0; /* always default to 0 */ | 903 | wlc->pub->unit); |
603 | 904 | *pdu = NULL; | |
604 | /* Handle retry limits */ | ||
605 | if (txrate[0].count <= rr_retry_limit) { | ||
606 | txrate[0].count++; | ||
607 | rr = true; | ||
608 | fbr = false; | ||
609 | } else { | ||
610 | fbr = true; | ||
611 | rr = false; | ||
612 | txrate[1].count++; | ||
613 | } | ||
614 | |||
615 | /* extract the length info */ | ||
616 | len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) | ||
617 | : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); | ||
618 | |||
619 | /* retrieve null delimiter count */ | ||
620 | ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; | ||
621 | seg_cnt += 1; | ||
622 | |||
623 | BCMMSG(wlc->wiphy, "wl%d: mpdu %d plcp_len %d\n", | ||
624 | wlc->pub->unit, count, len); | ||
625 | |||
626 | /* | ||
627 | * aggregateable mpdu. For ucode/hw agg, | ||
628 | * test whether need to break or change the epoch | ||
629 | */ | ||
630 | if (count == 0) { | ||
631 | mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); | ||
632 | /* refill the bits since might be a retx mpdu */ | ||
633 | mcl |= TXC_STARTMSDU; | ||
634 | rts = (struct ieee80211_rts *)&txh->rts_frame; | ||
635 | |||
636 | if (ieee80211_is_rts(rts->frame_control)) { | ||
637 | mcl |= TXC_SENDRTS; | ||
638 | use_rts = true; | ||
639 | } | ||
640 | if (ieee80211_is_cts(rts->frame_control)) { | ||
641 | mcl |= TXC_SENDCTS; | ||
642 | use_cts = true; | ||
643 | } | ||
644 | } else { | ||
645 | mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); | ||
646 | mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); | ||
647 | } | ||
648 | |||
649 | len = roundup(len, 4); | ||
650 | ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN); | ||
651 | |||
652 | dma_len += (u16) p->len; | ||
653 | |||
654 | BCMMSG(wlc->wiphy, "wl%d: ampdu_len %d" | ||
655 | " seg_cnt %d null delim %d\n", | ||
656 | wlc->pub->unit, ampdu_len, seg_cnt, ndelim); | ||
657 | |||
658 | txh->MacTxControlLow = cpu_to_le16(mcl); | ||
659 | |||
660 | /* this packet is added */ | ||
661 | pkt[count++] = p; | ||
662 | |||
663 | /* patch the first MPDU */ | ||
664 | if (count == 1) { | ||
665 | u8 plcp0, plcp3, is40, sgi; | ||
666 | |||
667 | if (rr) { | ||
668 | plcp0 = plcp[0]; | ||
669 | plcp3 = plcp[3]; | ||
670 | } else { | ||
671 | plcp0 = txh->FragPLCPFallback[0]; | ||
672 | plcp3 = txh->FragPLCPFallback[3]; | ||
673 | |||
674 | } | ||
675 | is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; | ||
676 | sgi = plcp3_issgi(plcp3) ? 1 : 0; | ||
677 | mcs = plcp0 & ~MIMO_PLCP_40MHZ; | ||
678 | max_ampdu_bytes = | ||
679 | min(scb_ampdu->max_rx_ampdu_bytes, | ||
680 | ampdu->max_txlen[mcs][is40][sgi]); | ||
681 | |||
682 | if (is40) | ||
683 | mimo_ctlchbw = | ||
684 | CHSPEC_SB_UPPER(wlc_phy_chanspec_get( | ||
685 | wlc->band->pi)) | ||
686 | ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; | ||
687 | |||
688 | /* rebuild the rspec and rspec_fallback */ | ||
689 | rspec = RSPEC_MIMORATE; | ||
690 | rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; | ||
691 | if (plcp[0] & MIMO_PLCP_40MHZ) | ||
692 | rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); | ||
693 | |||
694 | if (fbr_iscck) /* CCK */ | ||
695 | rspec_fallback = cck_rspec(cck_phy2mac_rate | ||
696 | (txh->FragPLCPFallback[0])); | ||
697 | else { /* MIMO */ | ||
698 | rspec_fallback = RSPEC_MIMORATE; | ||
699 | rspec_fallback |= | ||
700 | txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; | ||
701 | if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) | ||
702 | rspec_fallback |= | ||
703 | (PHY_TXC1_BW_40MHZ << | ||
704 | RSPEC_BW_SHIFT); | ||
705 | } | ||
706 | |||
707 | if (use_rts || use_cts) { | ||
708 | rts_rspec = | ||
709 | brcms_c_rspec_to_rts_rspec(wlc, | ||
710 | rspec, false, mimo_ctlchbw); | ||
711 | rts_rspec_fallback = | ||
712 | brcms_c_rspec_to_rts_rspec(wlc, | ||
713 | rspec_fallback, false, mimo_ctlchbw); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | /* if (first mpdu for host agg) */ | ||
718 | /* test whether to add more */ | ||
719 | if ((mcs_2_rate(mcs, true, false) >= f->dmaxferrate) && | ||
720 | (count == f->mcs2ampdu_table[mcs])) { | ||
721 | BCMMSG(wlc->wiphy, "wl%d: PR 37644: stopping" | ||
722 | " ampdu at %d for mcs %d\n", | ||
723 | wlc->pub->unit, count, mcs); | ||
724 | break; | 905 | break; |
725 | } | 906 | } |
726 | 907 | ||
727 | if (count == scb_ampdu->max_pdu) | 908 | seg_cnt += 1; |
728 | break; | ||
729 | 909 | ||
730 | /* | 910 | /* |
731 | * check to see if the next pkt is | 911 | * check to see if the next pkt is |
@@ -734,22 +914,13 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, | |||
734 | p = pktq_ppeek(&qi->q, prec); | 914 | p = pktq_ppeek(&qi->q, prec); |
735 | if (p) { | 915 | if (p) { |
736 | tx_info = IEEE80211_SKB_CB(p); | 916 | tx_info = IEEE80211_SKB_CB(p); |
737 | if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && | 917 | if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { |
738 | ((u8) (p->priority) == tid)) { | ||
739 | plen = p->len + AMPDU_MAX_MPDU_OVERHEAD; | ||
740 | plen = max(scb_ampdu->min_len, plen); | ||
741 | |||
742 | if ((plen + ampdu_len) > max_ampdu_bytes) { | ||
743 | p = NULL; | ||
744 | continue; | ||
745 | } | ||
746 | |||
747 | /* | 918 | /* |
748 | * check if there are enough | 919 | * check if there are enough |
749 | * descriptors available | 920 | * descriptors available |
750 | */ | 921 | */ |
751 | if (*wlc->core->txavail[fifo] <= seg_cnt + 1) { | 922 | if (*wlc->core->txavail[fifo] <= seg_cnt + 1) { |
752 | wiphy_err(wiphy, "%s: No fifo space " | 923 | wiphy_err(wiphy, "%s: No fifo space " |
753 | "!!\n", __func__); | 924 | "!!\n", __func__); |
754 | p = NULL; | 925 | p = NULL; |
755 | continue; | 926 | continue; |
@@ -762,111 +933,17 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, | |||
762 | } | 933 | } |
763 | } /* end while(p) */ | 934 | } /* end while(p) */ |
764 | 935 | ||
936 | count = skb_queue_len(&session.skb_list); | ||
765 | ini->tx_in_transit += count; | 937 | ini->tx_in_transit += count; |
766 | 938 | ||
767 | if (count) { | 939 | if (count) { |
768 | /* patch up the last txh */ | 940 | /* patch up first and last txh's */ |
769 | txh = (struct d11txh *) pkt[count - 1]->data; | 941 | brcms_c_ampdu_finalize(&session); |
770 | mcl = le16_to_cpu(txh->MacTxControlLow); | ||
771 | mcl &= ~TXC_AMPDU_MASK; | ||
772 | mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); | ||
773 | txh->MacTxControlLow = cpu_to_le16(mcl); | ||
774 | |||
775 | /* remove the null delimiter after last mpdu */ | ||
776 | ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; | ||
777 | txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; | ||
778 | ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; | ||
779 | |||
780 | /* remove the pad len from last mpdu */ | ||
781 | fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); | ||
782 | len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) | ||
783 | : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); | ||
784 | ampdu_len -= roundup(len, 4) - len; | ||
785 | |||
786 | /* patch up the first txh & plcp */ | ||
787 | txh = (struct d11txh *) pkt[0]->data; | ||
788 | plcp = (u8 *) (txh + 1); | ||
789 | |||
790 | BRCMS_SET_MIMO_PLCP_LEN(plcp, ampdu_len); | ||
791 | /* mark plcp to indicate ampdu */ | ||
792 | BRCMS_SET_MIMO_PLCP_AMPDU(plcp); | ||
793 | |||
794 | /* reset the mixed mode header durations */ | ||
795 | if (txh->MModeLen) { | ||
796 | u16 mmodelen = | ||
797 | brcms_c_calc_lsig_len(wlc, rspec, ampdu_len); | ||
798 | txh->MModeLen = cpu_to_le16(mmodelen); | ||
799 | preamble_type = BRCMS_MM_PREAMBLE; | ||
800 | } | ||
801 | if (txh->MModeFbrLen) { | ||
802 | u16 mmfbrlen = | ||
803 | brcms_c_calc_lsig_len(wlc, rspec_fallback, | ||
804 | ampdu_len); | ||
805 | txh->MModeFbrLen = cpu_to_le16(mmfbrlen); | ||
806 | fbr_preamble_type = BRCMS_MM_PREAMBLE; | ||
807 | } | ||
808 | |||
809 | /* set the preload length */ | ||
810 | if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { | ||
811 | dma_len = min(dma_len, f->ampdu_pld_size); | ||
812 | txh->PreloadSize = cpu_to_le16(dma_len); | ||
813 | } else | ||
814 | txh->PreloadSize = 0; | ||
815 | |||
816 | mch = le16_to_cpu(txh->MacTxControlHigh); | ||
817 | |||
818 | /* update RTS dur fields */ | ||
819 | if (use_rts || use_cts) { | ||
820 | u16 durid; | ||
821 | rts = (struct ieee80211_rts *)&txh->rts_frame; | ||
822 | if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == | ||
823 | TXC_PREAMBLE_RTS_MAIN_SHORT) | ||
824 | rts_preamble_type = BRCMS_SHORT_PREAMBLE; | ||
825 | |||
826 | if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == | ||
827 | TXC_PREAMBLE_RTS_FB_SHORT) | ||
828 | rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; | ||
829 | |||
830 | durid = | ||
831 | brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, | ||
832 | rspec, rts_preamble_type, | ||
833 | preamble_type, ampdu_len, | ||
834 | true); | ||
835 | rts->duration = cpu_to_le16(durid); | ||
836 | durid = brcms_c_compute_rtscts_dur(wlc, use_cts, | ||
837 | rts_rspec_fallback, | ||
838 | rspec_fallback, | ||
839 | rts_fbr_preamble_type, | ||
840 | fbr_preamble_type, | ||
841 | ampdu_len, true); | ||
842 | txh->RTSDurFallback = cpu_to_le16(durid); | ||
843 | /* set TxFesTimeNormal */ | ||
844 | txh->TxFesTimeNormal = rts->duration; | ||
845 | /* set fallback rate version of TxFesTimeNormal */ | ||
846 | txh->TxFesTimeFallback = txh->RTSDurFallback; | ||
847 | } | ||
848 | |||
849 | /* set flag and plcp for fallback rate */ | ||
850 | if (fbr) { | ||
851 | mch |= TXC_AMPDU_FBR; | ||
852 | txh->MacTxControlHigh = cpu_to_le16(mch); | ||
853 | BRCMS_SET_MIMO_PLCP_AMPDU(plcp); | ||
854 | BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); | ||
855 | } | ||
856 | |||
857 | BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n", | ||
858 | wlc->pub->unit, count, ampdu_len); | ||
859 | |||
860 | /* inform rate_sel if it this is a rate probe pkt */ | ||
861 | frameid = le16_to_cpu(txh->TxFrameID); | ||
862 | if (frameid & TXFID_RATE_PROBE_MASK) | ||
863 | wiphy_err(wiphy, "%s: XXX what to do with " | ||
864 | "TXFID_RATE_PROBE_MASK!?\n", __func__); | ||
865 | |||
866 | for (i = 0; i < count; i++) | ||
867 | brcms_c_txfifo(wlc, fifo, pkt[i], i == (count - 1), | ||
868 | ampdu->txpkt_weight); | ||
869 | 942 | ||
943 | while ((p = skb_dequeue(&session.skb_list)) != NULL) | ||
944 | brcms_c_txfifo(wlc, fifo, p, | ||
945 | skb_queue_empty(&session.skb_list), | ||
946 | ampdu->txpkt_weight); | ||
870 | } | 947 | } |
871 | /* endif (count) */ | 948 | /* endif (count) */ |
872 | return err; | 949 | return err; |
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h index 421f4ba7c63c..9a94923f850e 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h | |||
@@ -17,6 +17,32 @@ | |||
17 | #ifndef _BRCM_AMPDU_H_ | 17 | #ifndef _BRCM_AMPDU_H_ |
18 | #define _BRCM_AMPDU_H_ | 18 | #define _BRCM_AMPDU_H_ |
19 | 19 | ||
20 | /* | ||
21 | * Data structure representing an in-progress session for accumulating | ||
22 | * frames for AMPDU. | ||
23 | * | ||
24 | * wlc: pointer to common driver data | ||
25 | * skb_list: queue of skb's for AMPDU | ||
26 | * max_ampdu_len: maximum length for this AMPDU | ||
27 | * max_ampdu_frames: maximum number of frames for this AMPDU | ||
28 | * ampdu_len: total number of bytes accumulated for this AMPDU | ||
29 | * dma_len: DMA length of this AMPDU | ||
30 | */ | ||
31 | struct brcms_ampdu_session { | ||
32 | struct brcms_c_info *wlc; | ||
33 | struct sk_buff_head skb_list; | ||
34 | unsigned max_ampdu_len; | ||
35 | u16 max_ampdu_frames; | ||
36 | u16 ampdu_len; | ||
37 | u16 dma_len; | ||
38 | }; | ||
39 | |||
40 | extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, | ||
41 | struct brcms_c_info *wlc); | ||
42 | extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, | ||
43 | struct sk_buff *p); | ||
44 | extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); | ||
45 | |||
20 | extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); | 46 | extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); |
21 | extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); | 47 | extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); |
22 | extern int brcms_c_sendampdu(struct ampdu_info *ampdu, | 48 | extern int brcms_c_sendampdu(struct ampdu_info *ampdu, |