Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ef2c0512 authored by Seth Forshee's avatar Seth Forshee Committed by John W. Linville
Browse files

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: default avatarSeth Forshee <seth.forshee@canonical.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarArend van Spriel <arend@broadcom.com>
Tested-by: default avatarDaniel Wagner <wagi@monom.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent cb675f5f
Loading
Loading
Loading
Loading
+351 −274
Original line number Diff line number Diff line
@@ -498,141 +498,201 @@ brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
	scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes;
}

int
brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
	      struct sk_buff **pdu, int prec)
void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
				 struct brcms_c_info *wlc)
{
	struct brcms_c_info *wlc;
	struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
	u8 tid, ndelim;
	int err = 0;
	u8 preamble_type = BRCMS_GF_PREAMBLE;
	u8 fbr_preamble_type = BRCMS_GF_PREAMBLE;
	u8 rts_preamble_type = BRCMS_LONG_PREAMBLE;
	u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE;
	session->wlc = wlc;
	skb_queue_head_init(&session->skb_list);
	session->max_ampdu_len = 0;    /* determined from first MPDU */
	session->max_ampdu_frames = 0; /* determined from first MPDU */
	session->ampdu_len = 0;
	session->dma_len = 0;
}

	bool rr = true, fbr = false;
	uint i, count = 0, fifo, seg_cnt = 0;
	u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
	u32 ampdu_len, max_ampdu_bytes = 0;
	struct d11txh *txh = NULL;
/*
 * Preps the given packet for AMPDU based on the session data. If the
 * frame cannot be accomodated in the current session, -ENOSPC is
 * returned.
 */
int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
			    struct sk_buff *p)
{
	struct brcms_c_info *wlc = session->wlc;
	struct ampdu_info *ampdu = wlc->ampdu;
	struct scb *scb = &wlc->pri_scb;
	struct scb_ampdu *scb_ampdu = &scb->scb_ampdu;
	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
	struct ieee80211_tx_rate *txrate = tx_info->status.rates;
	struct d11txh *txh = (struct d11txh *)p->data;
	unsigned ampdu_frames;
	u8 ndelim, tid;
	u8 *plcp;
	struct ieee80211_hdr *h;
	struct scb *scb;
	struct scb_ampdu *scb_ampdu;
	struct scb_ampdu_tid_ini *ini;
	u8 mcs = 0;
	bool use_rts = false, use_cts = false;
	u32 rspec = 0, rspec_fallback = 0;
	u32 rts_rspec = 0, rts_rspec_fallback = 0;
	u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
	struct ieee80211_rts *rts;
	u8 rr_retry_limit;
	struct brcms_fifo_info *f;
	uint len;
	u16 mcl;
	bool fbr_iscck;
	struct ieee80211_tx_info *tx_info;
	u16 qlen;
	struct wiphy *wiphy;
	bool rr;

	wlc = ampdu->wlc;
	wiphy = wlc->wiphy;
	p = *pdu;
	ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
	plcp = (u8 *)(txh + 1);
	fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03);
	len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
			  BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
	len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN;

	tid = (u8) (p->priority);
	ampdu_frames = skb_queue_len(&session->skb_list);
	if (ampdu_frames != 0) {
		struct sk_buff *first;

	f = ampdu->fifo_tb + prio2fifo[tid];
		if (ampdu_frames + 1 > session->max_ampdu_frames ||
		    session->ampdu_len + len > session->max_ampdu_len)
			return -ENOSPC;

	scb = &wlc->pri_scb;
	scb_ampdu = &scb->scb_ampdu;
	ini = &scb_ampdu->ini[tid];

	/* Let pressure continue to build ... */
	qlen = pktq_plen(&qi->q, prec);
	if (ini->tx_in_transit > 0 &&
	    qlen < min(scb_ampdu->max_pdu, ini->ba_wsize))
		/* Collect multiple MPDU's to be sent in the next AMPDU */
		return -EBUSY;
		/*
		 * We aren't really out of space if the new frame is of
		 * a different priority, but we want the same behaviour
		 * so return -ENOSPC anyway.
		 *
		 * XXX: The old AMPDU code did this, but is it really
		 * necessary?
		 */
		first = skb_peek(&session->skb_list);
		if (p->priority != first->priority)
			return -ENOSPC;
	}

	/* at this point we intend to transmit an AMPDU */
	rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
	ampdu_len = 0;
	dma_len = 0;
	while (p) {
		struct ieee80211_tx_rate *txrate;
	/*
	 * Now that we're sure this frame can be accomodated, update the
	 * session information.
	 */
	session->ampdu_len += len;
	session->dma_len += p->len;

		tx_info = IEEE80211_SKB_CB(p);
		txrate = tx_info->status.rates;
	tid = (u8)p->priority;

		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
			err = brcms_c_prep_pdu(wlc, p, &fifo);
	/* Handle retry limits */
	if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) {
		txrate[0].count++;
		rr = true;
	} else {
			wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__);
			*pdu = NULL;
			err = 0;
			break;
		txrate[1].count++;
		rr = false;
	}

		if (err) {
			if (err == -EBUSY) {
				wiphy_err(wiphy, "wl%d: sendampdu: "
					  "prep_xdu retry; seq 0x%x\n",
					  wlc->pub->unit, seq);
				*pdu = p;
				break;
			}
	if (ampdu_frames == 0) {
		u8 plcp0, plcp3, is40, sgi, mcs;
		uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
		struct brcms_fifo_info *f = &ampdu->fifo_tb[fifo];

		if (rr) {
			plcp0 = plcp[0];
			plcp3 = plcp[3];
		} else {
			plcp0 = txh->FragPLCPFallback[0];
			plcp3 = txh->FragPLCPFallback[3];

			/* error in the packet; reject it */
			wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu "
				  "rejected; seq 0x%x\n", wlc->pub->unit, seq);
			*pdu = NULL;
			break;
		}

		/* pkt is good to be aggregated */
		txh = (struct d11txh *) p->data;
		plcp = (u8 *) (txh + 1);
		h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
		seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
		index = TX_SEQ_TO_INDEX(seq);
		/* Limit AMPDU size based on MCS */
		is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
		sgi = plcp3_issgi(plcp3) ? 1 : 0;
		mcs = plcp0 & ~MIMO_PLCP_40MHZ;
		session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes,
					     ampdu->max_txlen[mcs][is40][sgi]);

		session->max_ampdu_frames = scb_ampdu->max_pdu;
		if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
			session->max_ampdu_frames =
				min_t(u16, f->mcs2ampdu_table[mcs],
				      session->max_ampdu_frames);
		}
	}

		/* check mcl fields and test whether it can be agg'd */
	/*
	 * Treat all frames as "middle" frames of AMPDU here. First and
	 * last frames must be fixed up after all MPDUs have been prepped.
	 */
	mcl = le16_to_cpu(txh->MacTxControlLow);
	mcl &= ~TXC_AMPDU_MASK;
		fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3);
	mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
	mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
	txh->MacTxControlLow = cpu_to_le16(mcl);
	txh->PreloadSize = 0;	/* always default to 0 */

		/*  Handle retry limits */
		if (txrate[0].count <= rr_retry_limit) {
			txrate[0].count++;
			rr = true;
			fbr = false;
		} else {
			fbr = true;
			rr = false;
			txrate[1].count++;
	skb_queue_tail(&session->skb_list, p);

	return 0;
}

		/* extract the length info */
		len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
		    : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session)
{
	struct brcms_c_info *wlc = session->wlc;
	struct ampdu_info *ampdu = wlc->ampdu;
	struct sk_buff *first, *last;
	struct d11txh *txh;
	struct ieee80211_tx_info *tx_info;
	struct ieee80211_tx_rate *txrate;
	u8 ndelim;
	u8 *plcp;
	uint len;
	uint fifo;
	struct brcms_fifo_info *f;
	u16 mcl;
	bool fbr;
	bool fbr_iscck;
	struct ieee80211_rts *rts;
	bool use_rts = false, use_cts = false;
	u16 dma_len = session->dma_len;
	u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
	u32 rspec = 0, rspec_fallback = 0;
	u32 rts_rspec = 0, rts_rspec_fallback = 0;
	u8 plcp0, plcp3, is40, sgi, mcs;
	u16 mch;
	u8 preamble_type = BRCMS_GF_PREAMBLE;
	u8 fbr_preamble_type = BRCMS_GF_PREAMBLE;
	u8 rts_preamble_type = BRCMS_LONG_PREAMBLE;
	u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE;

	if (skb_queue_empty(&session->skb_list))
		return;

	first = skb_peek(&session->skb_list);
	last = skb_peek_tail(&session->skb_list);

		/* retrieve null delimiter count */
	/* Need to fix up last MPDU first to adjust AMPDU length */
	txh = (struct d11txh *)last->data;
	fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK;
	f = &ampdu->fifo_tb[fifo];

	mcl = le16_to_cpu(txh->MacTxControlLow);
	mcl &= ~TXC_AMPDU_MASK;
	mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
	txh->MacTxControlLow = cpu_to_le16(mcl);

	/* remove the null delimiter after last mpdu */
	ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
		seg_cnt += 1;
	txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
	session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;

	/* remove the pad len from last mpdu */
	fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
	len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) :
			  BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
	session->ampdu_len -= roundup(len, 4) - len;

		BCMMSG(wlc->wiphy, "wl%d: mpdu %d plcp_len %d\n",
			wlc->pub->unit, count, len);
	/* Now fix up the first MPDU */
	tx_info = IEEE80211_SKB_CB(first);
	txrate = tx_info->status.rates;
	txh = (struct d11txh *)first->data;
	plcp = (u8 *)(txh + 1);
	rts = (struct ieee80211_rts *)&txh->rts_frame;

		/*
		 * aggregateable mpdu. For ucode/hw agg,
		 * test whether need to break or change the epoch
		 */
		if (count == 0) {
	mcl = le16_to_cpu(txh->MacTxControlLow);
	/* If only one MPDU leave it marked as last */
	if (first != last) {
		mcl &= ~TXC_AMPDU_MASK;
		mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
			/* refill the bits since might be a retx mpdu */
	}
	mcl |= TXC_STARTMSDU;
			rts = (struct ieee80211_rts *)&txh->rts_frame;

	if (ieee80211_is_rts(rts->frame_control)) {
		mcl |= TXC_SENDRTS;
		use_rts = true;
@@ -641,49 +701,26 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
		mcl |= TXC_SENDCTS;
		use_cts = true;
	}
		} else {
			mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
			mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
		}

		len = roundup(len, 4);
		ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);

		dma_len += (u16) p->len;

		BCMMSG(wlc->wiphy, "wl%d: ampdu_len %d"
			" seg_cnt %d null delim %d\n",
			wlc->pub->unit, ampdu_len, seg_cnt, ndelim);

	txh->MacTxControlLow = cpu_to_le16(mcl);

		/* this packet is added */
		pkt[count++] = p;

		/* patch the first MPDU */
		if (count == 1) {
			u8 plcp0, plcp3, is40, sgi;

			if (rr) {
	fbr = txrate[1].count > 0;
	if (!fbr) {
		plcp0 = plcp[0];
		plcp3 = plcp[3];
	} else {
		plcp0 = txh->FragPLCPFallback[0];
		plcp3 = txh->FragPLCPFallback[3];

	}
	is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
	sgi = plcp3_issgi(plcp3) ? 1 : 0;
	mcs = plcp0 & ~MIMO_PLCP_40MHZ;
			max_ampdu_bytes =
			    min(scb_ampdu->max_rx_ampdu_bytes,
				ampdu->max_txlen[mcs][is40][sgi]);

			if (is40)
				mimo_ctlchbw =
				   CHSPEC_SB_UPPER(wlc_phy_chanspec_get(
								 wlc->band->pi))
				   ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
	if (is40) {
		if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi)))
			mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP;
		else
			mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
	}

	/* rebuild the rspec and rspec_fallback */
	rspec = RSPEC_MIMORATE;
@@ -691,117 +728,40 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
	if (plcp[0] & MIMO_PLCP_40MHZ)
		rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);

			if (fbr_iscck)	/* CCK */
				rspec_fallback = cck_rspec(cck_phy2mac_rate
						    (txh->FragPLCPFallback[0]));
			else {	/* MIMO */
	fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03);
	if (fbr_iscck) {
		rspec_fallback =
			cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0]));
	} else {
		rspec_fallback = RSPEC_MIMORATE;
				rspec_fallback |=
				    txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
		rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
		if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
					rspec_fallback |=
					    (PHY_TXC1_BW_40MHZ <<
					     RSPEC_BW_SHIFT);
			rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT;
	}

	if (use_rts || use_cts) {
		rts_rspec =
				    brcms_c_rspec_to_rts_rspec(wlc,
					rspec, false, mimo_ctlchbw);
			brcms_c_rspec_to_rts_rspec(wlc, rspec,
						   false, mimo_ctlchbw);
		rts_rspec_fallback =
				    brcms_c_rspec_to_rts_rspec(wlc,
					rspec_fallback, false, mimo_ctlchbw);
			}
		}

		/* if (first mpdu for host agg) */
		/* test whether to add more */
		if ((mcs_2_rate(mcs, true, false) >= f->dmaxferrate) &&
		    (count == f->mcs2ampdu_table[mcs])) {
			BCMMSG(wlc->wiphy, "wl%d: PR 37644: stopping"
				" ampdu at %d for mcs %d\n",
				wlc->pub->unit, count, mcs);
			break;
			brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback,
						   false, mimo_ctlchbw);
	}

		if (count == scb_ampdu->max_pdu)
			break;

		/*
		 * check to see if the next pkt is
		 * a candidate for aggregation
		 */
		p = pktq_ppeek(&qi->q, prec);
		if (p) {
			tx_info = IEEE80211_SKB_CB(p);
			if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
			    ((u8) (p->priority) == tid)) {
				plen = p->len + AMPDU_MAX_MPDU_OVERHEAD;
				plen = max(scb_ampdu->min_len, plen);

				if ((plen + ampdu_len) > max_ampdu_bytes) {
					p = NULL;
					continue;
				}

				/*
				 * check if there are enough
				 * descriptors available
				 */
				if (*wlc->core->txavail[fifo] <= seg_cnt + 1) {
					wiphy_err(wiphy, "%s: No fifo space  "
						  "!!\n", __func__);
					p = NULL;
					continue;
				}
				/* next packet fit for aggregation so dequeue */
				p = brcmu_pktq_pdeq(&qi->q, prec);
			} else {
				p = NULL;
			}
		}
	}			/* end while(p) */

	ini->tx_in_transit += count;

	if (count) {
		/* patch up the last txh */
		txh = (struct d11txh *) pkt[count - 1]->data;
		mcl = le16_to_cpu(txh->MacTxControlLow);
		mcl &= ~TXC_AMPDU_MASK;
		mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
		txh->MacTxControlLow = cpu_to_le16(mcl);

		/* remove the null delimiter after last mpdu */
		ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
		txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
		ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;

		/* remove the pad len from last mpdu */
		fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
		len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
		    : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
		ampdu_len -= roundup(len, 4) - len;

		/* patch up the first txh & plcp */
		txh = (struct d11txh *) pkt[0]->data;
		plcp = (u8 *) (txh + 1);

		BRCMS_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
	BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len);
	/* mark plcp to indicate ampdu */
	BRCMS_SET_MIMO_PLCP_AMPDU(plcp);

	/* reset the mixed mode header durations */
	if (txh->MModeLen) {
			u16 mmodelen =
			    brcms_c_calc_lsig_len(wlc, rspec, ampdu_len);
		u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec,
						     session->ampdu_len);
		txh->MModeLen = cpu_to_le16(mmodelen);
		preamble_type = BRCMS_MM_PREAMBLE;
	}
	if (txh->MModeFbrLen) {
			u16 mmfbrlen =
			    brcms_c_calc_lsig_len(wlc, rspec_fallback,
						  ampdu_len);
		u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback,
						     session->ampdu_len);
		txh->MModeFbrLen = cpu_to_le16(mmfbrlen);
		fbr_preamble_type = BRCMS_MM_PREAMBLE;
	}
@@ -810,15 +770,15 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
	if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) {
		dma_len = min(dma_len, f->ampdu_pld_size);
		txh->PreloadSize = cpu_to_le16(dma_len);
		} else
	} else {
		txh->PreloadSize = 0;
	}

	mch = le16_to_cpu(txh->MacTxControlHigh);

	/* update RTS dur fields */
	if (use_rts || use_cts) {
		u16 durid;
			rts = (struct ieee80211_rts *)&txh->rts_frame;
		if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
		    TXC_PREAMBLE_RTS_MAIN_SHORT)
			rts_preamble_type = BRCMS_SHORT_PREAMBLE;
@@ -827,18 +787,17 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
		     TXC_PREAMBLE_RTS_FB_SHORT)
			rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE;

			durid =
			    brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec,
		durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec,
						   rspec, rts_preamble_type,
						   preamble_type, ampdu_len,
						   true);
						   preamble_type,
						   session->ampdu_len, true);
		rts->duration = cpu_to_le16(durid);
		durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
						   rts_rspec_fallback,
						   rspec_fallback,
						   rts_fbr_preamble_type,
						   fbr_preamble_type,
						       ampdu_len, true);
						   session->ampdu_len, true);
		txh->RTSDurFallback = cpu_to_le16(durid);
		/* set TxFesTimeNormal */
		txh->TxFesTimeNormal = rts->duration;
@@ -855,18 +814,136 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
	}

	BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n",
			wlc->pub->unit, count, ampdu_len);
		wlc->pub->unit, skb_queue_len(&session->skb_list),
		session->ampdu_len);
}

		/* inform rate_sel if it this is a rate probe pkt */
		frameid = le16_to_cpu(txh->TxFrameID);
		if (frameid & TXFID_RATE_PROBE_MASK)
			wiphy_err(wiphy, "%s: XXX what to do with "
				  "TXFID_RATE_PROBE_MASK!?\n", __func__);
int
brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
	      struct sk_buff **pdu, int prec)
{
	struct brcms_c_info *wlc;
	struct sk_buff *p;
	struct brcms_ampdu_session session;
	int err = 0;
	u8 tid;

		for (i = 0; i < count; i++)
			brcms_c_txfifo(wlc, fifo, pkt[i], i == (count - 1),
				   ampdu->txpkt_weight);
	uint count, fifo, seg_cnt = 0;
	struct scb *scb;
	struct scb_ampdu *scb_ampdu;
	struct scb_ampdu_tid_ini *ini;
	struct brcms_fifo_info *f;
	struct ieee80211_tx_info *tx_info;
	u16 qlen;
	struct wiphy *wiphy;

	wlc = ampdu->wlc;
	wiphy = wlc->wiphy;
	p = *pdu;

	tid = (u8) (p->priority);

	f = ampdu->fifo_tb + prio2fifo[tid];

	scb = &wlc->pri_scb;
	scb_ampdu = &scb->scb_ampdu;
	ini = &scb_ampdu->ini[tid];

	/* Let pressure continue to build ... */
	qlen = pktq_plen(&qi->q, prec);
	if (ini->tx_in_transit > 0 &&
	    qlen < min(scb_ampdu->max_pdu, ini->ba_wsize))
		/* Collect multiple MPDU's to be sent in the next AMPDU */
		return -EBUSY;

	/* at this point we intend to transmit an AMPDU */
	brcms_c_ampdu_reset_session(&session, wlc);

	while (p) {
		struct ieee80211_tx_rate *txrate;

		tx_info = IEEE80211_SKB_CB(p);
		txrate = tx_info->status.rates;

		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
			err = brcms_c_prep_pdu(wlc, p, &fifo);
		} else {
			wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__);
			*pdu = NULL;
			err = 0;
			break;
		}

		if (err) {
			if (err == -EBUSY) {
				wiphy_err(wiphy, "wl%d: sendampdu: "
					  "prep_xdu retry\n", wlc->pub->unit);
				*pdu = p;
				break;
			}

			/* error in the packet; reject it */
			wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu "
				  "rejected\n", wlc->pub->unit);
			*pdu = NULL;
			break;
		}

		err = brcms_c_ampdu_add_frame(&session, p);
		if (err == -ENOSPC) {
			/*
			 * No space for this packet in the AMPDU.
			 * Requeue packet and proceed;
			 */
			*pdu = p;
			break;
		} else if (err) {
			/* Unexpected error; reject packet */
			wiphy_err(wiphy, "wl%d: sendampdu: add_frame rejected",
				  wlc->pub->unit);
			*pdu = NULL;
			break;
		}

		seg_cnt += 1;

		/*
		 * check to see if the next pkt is
		 * a candidate for aggregation
		 */
		p = pktq_ppeek(&qi->q, prec);
		if (p) {
			tx_info = IEEE80211_SKB_CB(p);
			if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
				/*
				 * check if there are enough
				 * descriptors available
				 */
				if (*wlc->core->txavail[fifo] <= seg_cnt + 1) {
					wiphy_err(wiphy, "%s: No fifo space "
						  "!!\n", __func__);
					p = NULL;
					continue;
				}
				/* next packet fit for aggregation so dequeue */
				p = brcmu_pktq_pdeq(&qi->q, prec);
			} else {
				p = NULL;
			}
		}
	}			/* end while(p) */

	count = skb_queue_len(&session.skb_list);
	ini->tx_in_transit += count;

	if (count) {
		/* patch up first and last txh's */
		brcms_c_ampdu_finalize(&session);

		while ((p = skb_dequeue(&session.skb_list)) != NULL)
			brcms_c_txfifo(wlc, fifo, p,
				       skb_queue_empty(&session.skb_list),
				       ampdu->txpkt_weight);
	}
	/* endif (count) */
	return err;
+26 −0
Original line number Diff line number Diff line
@@ -17,6 +17,32 @@
#ifndef _BRCM_AMPDU_H_
#define _BRCM_AMPDU_H_

/*
 * Data structure representing an in-progress session for accumulating
 * frames for AMPDU.
 *
 * wlc: pointer to common driver data
 * skb_list: queue of skb's for AMPDU
 * max_ampdu_len: maximum length for this AMPDU
 * max_ampdu_frames: maximum number of frames for this AMPDU
 * ampdu_len: total number of bytes accumulated for this AMPDU
 * dma_len: DMA length of this AMPDU
 */
struct brcms_ampdu_session {
	struct brcms_c_info *wlc;
	struct sk_buff_head skb_list;
	unsigned max_ampdu_len;
	u16 max_ampdu_frames;
	u16 ampdu_len;
	u16 dma_len;
};

extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session,
					struct brcms_c_info *wlc);
extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session,
				   struct sk_buff *p);
extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session);

extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc);
extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu);
extern int brcms_c_sendampdu(struct ampdu_info *ampdu,