aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorSeth Forshee <seth.forshee@canonical.com>2012-11-15 09:07:51 -0500
committerJohn W. Linville <linville@tuxdriver.com>2012-11-20 14:03:06 -0500
commitef2c0512bbf43440a78a6471059e19641eab1e83 (patch)
treed1df3c2350070ff3a62ba49c65e799f40e551fbf /drivers/net/wireless
parentcb675f5ff39e12836591246e730e11ce648048d2 (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.c625
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/ampdu.h26
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
501void 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 */
517int 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 = &ampdu->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
626void 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 = &ampdu->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
501int 821int
502brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, 822brcms_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 */
31struct 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
40extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
41 struct brcms_c_info *wlc);
42extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
43 struct sk_buff *p);
44extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session);
45
20extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); 46extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc);
21extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); 47extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu);
22extern int brcms_c_sendampdu(struct ampdu_info *ampdu, 48extern int brcms_c_sendampdu(struct ampdu_info *ampdu,