Enscript Output

extractedLnx/linux-2.6.37/drivers/staging/brcm80211/sys/wlc_mac80211.c_wlc_d11hdrs_mac80211.c

static u16 BCMFASTPATH
wlc_d11hdrs_mac80211(wlc_info_t *wlc, struct ieee80211_hw *hw,
		     void *p, struct scb *scb, uint frag,
		     uint nfrags, uint queue, uint next_frag_len,
		     wsec_key_t *key, ratespec_t rspec_override)
{
	struct dot11_header *h;
	d11txh_t *txh;
	u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN];
	osl_t *osh;
	int len, phylen, rts_phylen;
	u16 fc, type, frameid, mch, phyctl, xfts, mainrates;
	u16 seq = 0, mcl = 0, status = 0;
	ratespec_t rspec[2] = { WLC_RATE_1M, WLC_RATE_1M }, rts_rspec[2] = {
	WLC_RATE_1M, WLC_RATE_1M};
	bool use_rts = false;
	bool use_cts = false;
	bool use_rifs = false;
	bool short_preamble[2] = { false, false };
	u8 preamble_type[2] = { WLC_LONG_PREAMBLE, WLC_LONG_PREAMBLE };
	u8 rts_preamble_type[2] = { WLC_LONG_PREAMBLE, WLC_LONG_PREAMBLE };
	u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN];
	struct dot11_rts_frame *rts = NULL;
	bool qos;
	uint ac;
	u32 rate_val[2];
	bool hwtkmic = false;
	u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
#ifdef WLANTSEL
#define ANTCFG_NONE 0xFF
	u8 antcfg = ANTCFG_NONE;
	u8 fbantcfg = ANTCFG_NONE;
#endif
	uint phyctl1_stf = 0;
	u16 durid = 0;
	struct ieee80211_tx_rate *txrate[2];
	int k;
	struct ieee80211_tx_info *tx_info;
	bool is_mcs[2];
	u16 mimo_txbw;
	u8 mimo_preamble_type;

	frameid = 0;

	ASSERT(queue < NFIFO);

	osh = wlc->osh;

	/* locate 802.11 MAC header */
	h = (struct dot11_header *)PKTDATA(p);
	fc = ltoh16(h->fc);
	type = FC_TYPE(fc);

	qos = (type == FC_TYPE_DATA && FC_SUBTYPE_ANY_QOS(FC_SUBTYPE(fc)));

	/* compute length of frame in bytes for use in PLCP computations */
	len = pkttotlen(osh, p);
	phylen = len + DOT11_FCS_LEN;

	/* If WEP enabled, add room in phylen for the additional bytes of
	 * ICV which MAC generates.  We do NOT add the additional bytes to
	 * the packet itself, thus phylen = packet length + ICV_LEN + FCS_LEN
	 * in this case
	 */
	if (key) {
		phylen += key->icv_len;
	}

	/* Get tx_info */
	tx_info = IEEE80211_SKB_CB(p);
	ASSERT(tx_info);

	/* add PLCP */
	plcp = PKTPUSH(p, D11_PHY_HDR_LEN);

	/* add Broadcom tx descriptor header */
	txh = (d11txh_t *) PKTPUSH(p, D11_TXH_LEN);
	bzero((char *)txh, D11_TXH_LEN);

	/* setup frameid */
	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
		/* non-AP STA should never use BCMC queue */
		ASSERT(queue != TX_BCMC_FIFO);
		if (queue == TX_BCMC_FIFO) {
			WL_ERROR(("wl%d: %s: ASSERT queue == TX_BCMC!\n",
				  WLCWLUNIT(wlc), __func__));
			frameid = bcmc_fid_generate(wlc, NULL, txh);
		} else {
			/* Increment the counter for first fragment */
			if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) {
				SCB_SEQNUM(scb, PKTPRIO(p))++;
			}

			/* extract fragment number from frame first */
			seq = ltoh16(seq) & FRAGNUM_MASK;
			seq |= (SCB_SEQNUM(scb, PKTPRIO(p)) << SEQNUM_SHIFT);
			h->seq = htol16(seq);

			frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
			    (queue & TXFID_QUEUE_MASK);
		}
	}
	frameid |= queue & TXFID_QUEUE_MASK;

	/* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */
	if (SCB_PS(scb) || ((fc & FC_KIND_MASK) == FC_BEACON))
		mcl |= TXC_IGNOREPMQ;

	ASSERT(hw->max_rates <= IEEE80211_TX_MAX_RATES);
	ASSERT(hw->max_rates == 2);

	txrate[0] = tx_info->control.rates;
	txrate[1] = txrate[0] + 1;

	ASSERT(txrate[0]->idx >= 0);
	/* if rate control algorithm didn't give us a fallback rate, use the primary rate */
	if (txrate[1]->idx < 0) {
		txrate[1] = txrate[0];
	}
#ifdef WLC_HIGH_ONLY
	/* Double protection , just in case */
	if (txrate[0]->idx > HIGHEST_SINGLE_STREAM_MCS)
		txrate[0]->idx = HIGHEST_SINGLE_STREAM_MCS;
	if (txrate[1]->idx > HIGHEST_SINGLE_STREAM_MCS)
		txrate[1]->idx = HIGHEST_SINGLE_STREAM_MCS;
#endif

	for (k = 0; k < hw->max_rates; k++) {
		is_mcs[k] =
		    txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false;
		if (!is_mcs[k]) {
			ASSERT(!(tx_info->flags & IEEE80211_TX_CTL_AMPDU));
			if ((txrate[k]->idx >= 0)
			    && (txrate[k]->idx <
				hw->wiphy->bands[tx_info->band]->n_bitrates)) {
				rate_val[k] =
				    hw->wiphy->bands[tx_info->band]->
				    bitrates[txrate[k]->idx].hw_value;
				short_preamble[k] =
				    txrate[k]->
				    flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ?
				    true : false;
			} else {
				ASSERT((txrate[k]->idx >= 0) &&
				       (txrate[k]->idx <
					hw->wiphy->bands[tx_info->band]->
					n_bitrates));
				rate_val[k] = WLC_RATE_1M;
			}
		} else {
			rate_val[k] = txrate[k]->idx;
		}
		/* Currently only support same setting for primay and fallback rates.
		 * Unify flags for each rate into a single value for the frame
		 */
		use_rts |=
		    txrate[k]->
		    flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false;
		use_cts |=
		    txrate[k]->
		    flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false;

		if (is_mcs[k])
			rate_val[k] |= NRATE_MCS_INUSE;

		rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, rate_val[k]);

		/* (1) RATE: determine and validate primary rate and fallback rates */
		if (!RSPEC_ACTIVE(rspec[k])) {
			ASSERT(RSPEC_ACTIVE(rspec[k]));
			rspec[k] = WLC_RATE_1M;
		} else {
			if (WLANTSEL_ENAB(wlc) && !ETHER_ISMULTI(&h->a1)) {
				/* set tx antenna config */
				wlc_antsel_antcfg_get(wlc->asi, false, false, 0,
						      0, &antcfg, &fbantcfg);
			}
		}
	}

	phyctl1_stf = wlc->stf->ss_opmode;

	if (N_ENAB(wlc->pub)) {
		for (k = 0; k < hw->max_rates; k++) {
			/* apply siso/cdd to single stream mcs's or ofdm if rspec is auto selected */
			if (((IS_MCS(rspec[k]) &&
			      IS_SINGLE_STREAM(rspec[k] & RSPEC_RATE_MASK)) ||
			     IS_OFDM(rspec[k]))
			    && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY)
				|| !(rspec[k] & RSPEC_OVERRIDE))) {
				rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK);

				/* For SISO MCS use STBC if possible */
				if (IS_MCS(rspec[k])
				    && WLC_STF_SS_STBC_TX(wlc, scb)) {
					u8 stc;

					ASSERT(WLC_STBC_CAP_PHY(wlc));
					stc = 1;	/* Nss for single stream is always 1 */
					rspec[k] |=
					    (PHY_TXC1_MODE_STBC <<
					     RSPEC_STF_SHIFT) | (stc <<
								 RSPEC_STC_SHIFT);
				} else
					rspec[k] |=
					    (phyctl1_stf << RSPEC_STF_SHIFT);
			}

			/* Is the phy configured to use 40MHZ frames? If so then pick the desired txbw */
			if (CHSPEC_WLC_BW(wlc->chanspec) == WLC_40_MHZ) {
				/* default txbw is 20in40 SB */
				mimo_ctlchbw = mimo_txbw =
				    CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
				    ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;

				if (IS_MCS(rspec[k])) {
					/* mcs 32 must be 40b/w DUP */
					if ((rspec[k] & RSPEC_RATE_MASK) == 32) {
						mimo_txbw =
						    PHY_TXC1_BW_40MHZ_DUP;
						/* use override */
					} else if (wlc->mimo_40txbw != AUTO)
						mimo_txbw = wlc->mimo_40txbw;
					/* else check if dst is using 40 Mhz */
					else if (scb->flags & SCB_IS40)
						mimo_txbw = PHY_TXC1_BW_40MHZ;
				} else if (IS_OFDM(rspec[k])) {
					if (wlc->ofdm_40txbw != AUTO)
						mimo_txbw = wlc->ofdm_40txbw;
				} else {
					ASSERT(IS_CCK(rspec[k]));
					if (wlc->cck_40txbw != AUTO)
						mimo_txbw = wlc->cck_40txbw;
				}
			} else {
				/* mcs32 is 40 b/w only.
				 * This is possible for probe packets on a STA during SCAN
				 */
				if ((rspec[k] & RSPEC_RATE_MASK) == 32) {
					/* mcs 0 */
					rspec[k] = RSPEC_MIMORATE;
				}
				mimo_txbw = PHY_TXC1_BW_20MHZ;
			}

			/* Set channel width */
			rspec[k] &= ~RSPEC_BW_MASK;
			if ((k == 0) || ((k > 0) && IS_MCS(rspec[k])))
				rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT);
			else
				rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT);

			/* Set Short GI */
#ifdef NOSGIYET
			if (IS_MCS(rspec[k])
			    && (txrate[k]->flags & IEEE80211_TX_RC_SHORT_GI))
				rspec[k] |= RSPEC_SHORT_GI;
			else if (!(txrate[k]->flags & IEEE80211_TX_RC_SHORT_GI))
				rspec[k] &= ~RSPEC_SHORT_GI;
#else
			rspec[k] &= ~RSPEC_SHORT_GI;
#endif

			mimo_preamble_type = WLC_MM_PREAMBLE;
			if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD)
				mimo_preamble_type = WLC_GF_PREAMBLE;

			if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
			    && (!IS_MCS(rspec[k]))) {
				WL_ERROR(("wl%d: %s: IEEE80211_TX_RC_MCS != IS_MCS(rspec)\n", WLCWLUNIT(wlc), __func__));
				ASSERT(0 && "Rate mismatch");
			}

			if (IS_MCS(rspec[k])) {
				preamble_type[k] = mimo_preamble_type;

				/* if SGI is selected, then forced mm for single stream */
				if ((rspec[k] & RSPEC_SHORT_GI)
				    && IS_SINGLE_STREAM(rspec[k] &
							RSPEC_RATE_MASK)) {
					preamble_type[k] = WLC_MM_PREAMBLE;
				}
			}

			/* mimo bw field MUST now be valid in the rspec (it affects duration calculations) */
			ASSERT(VALID_RATE_DBG(wlc, rspec[0]));

			/* should be better conditionalized */
			if (!IS_MCS(rspec[0])
			    && (tx_info->control.rates[0].
				flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
				preamble_type[k] = WLC_SHORT_PREAMBLE;

			ASSERT(!IS_MCS(rspec[0])
			       || WLC_IS_MIMO_PREAMBLE(preamble_type[k]));
		}
	} else {
		for (k = 0; k < hw->max_rates; k++) {
			/* Set ctrlchbw as 20Mhz */
			ASSERT(!IS_MCS(rspec[k]));
			rspec[k] &= ~RSPEC_BW_MASK;
			rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT);

			/* for nphy, stf of ofdm frames must follow policies */
			if (WLCISNPHY(wlc->band) && IS_OFDM(rspec[k])) {
				rspec[k] &= ~RSPEC_STF_MASK;
				rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT;
			}
		}
	}

	/* Reset these for use with AMPDU's */
	txrate[0]->count = 0;
	txrate[1]->count = 0;

	/* (3) PLCP: determine PLCP header and MAC duration, fill d11txh_t */
	wlc_compute_plcp(wlc, rspec[0], phylen, plcp);
	wlc_compute_plcp(wlc, rspec[1], phylen, plcp_fallback);
	bcopy(plcp_fallback, (char *)&txh->FragPLCPFallback,
	      sizeof(txh->FragPLCPFallback));

	/* Length field now put in CCK FBR CRC field */
	if (IS_CCK(rspec[1])) {
		txh->FragPLCPFallback[4] = phylen & 0xff;
		txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8;
	}

	/* MIMO-RATE: need validation ?? */
	mainrates =
	    IS_OFDM(rspec[0]) ? D11A_PHY_HDR_GRATE((ofdm_phy_hdr_t *) plcp) :
	    plcp[0];

	/* DUR field for main rate */
	if ((fc != FC_PS_POLL) && !ETHER_ISMULTI(&h->a1) && !use_rifs) {
		durid =
		    wlc_compute_frame_dur(wlc, rspec[0], preamble_type[0],
					  next_frag_len);
		h->durid = htol16(durid);
	} else if (use_rifs) {
		/* NAV protect to end of next max packet size */
		durid =
		    (u16) wlc_calc_frame_time(wlc, rspec[0],
						 preamble_type[0],
						 DOT11_MAX_FRAG_LEN);
		durid += RIFS_11N_TIME;
		h->durid = htol16(durid);
	}

	/* DUR field for fallback rate */
	if (fc == FC_PS_POLL)
		txh->FragDurFallback = h->durid;
	else if (ETHER_ISMULTI(&h->a1) || use_rifs)
		txh->FragDurFallback = 0;
	else {
		durid = wlc_compute_frame_dur(wlc, rspec[1],
					      preamble_type[1], next_frag_len);
		txh->FragDurFallback = htol16(durid);
	}

	/* (4) MAC-HDR: MacTxControlLow */
	if (frag == 0)
		mcl |= TXC_STARTMSDU;

	if (!ETHER_ISMULTI(&h->a1))
		mcl |= TXC_IMMEDACK;

	if (BAND_5G(wlc->band->bandtype))
		mcl |= TXC_FREQBAND_5G;

	if (CHSPEC_IS40(WLC_BAND_PI_RADIO_CHANSPEC))
		mcl |= TXC_BW_40;

	/* set AMIC bit if using hardware TKIP MIC */
	if (hwtkmic)
		mcl |= TXC_AMIC;

	txh->MacTxControlLow = htol16(mcl);

	/* MacTxControlHigh */
	mch = 0;

	/* Set fallback rate preamble type */
	if ((preamble_type[1] == WLC_SHORT_PREAMBLE) ||
	    (preamble_type[1] == WLC_GF_PREAMBLE)) {
		ASSERT((preamble_type[1] == WLC_GF_PREAMBLE) ||
		       (!IS_MCS(rspec[1])));
		if (RSPEC2RATE(rspec[1]) != WLC_RATE_1M)
			mch |= TXC_PREAMBLE_DATA_FB_SHORT;
	}

	/* MacFrameControl */
	bcopy((char *)&h->fc, (char *)&txh->MacFrameControl, sizeof(u16));

	txh->TxFesTimeNormal = htol16(0);

	txh->TxFesTimeFallback = htol16(0);

	/* TxFrameRA */
	bcopy((char *)&h->a1, (char *)&txh->TxFrameRA, ETHER_ADDR_LEN);

	/* TxFrameID */
	txh->TxFrameID = htol16(frameid);

	/* TxStatus, Note the case of recreating the first frag of a suppressed frame
	 * then we may need to reset the retry cnt's via the status reg
	 */
	txh->TxStatus = htol16(status);

	if (D11REV_GE(wlc->pub->corerev, 16)) {
		/* extra fields for ucode AMPDU aggregation, the new fields are added to
		 * the END of previous structure so that it's compatible in driver.
		 * In old rev ucode, these fields should be ignored
		 */
		txh->MaxNMpdus = htol16(0);
		txh->MaxABytes_MRT = htol16(0);
		txh->MaxABytes_FBR = htol16(0);
		txh->MinMBytes = htol16(0);
	}

	/* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, furnish d11txh_t */
	/* RTS PLCP header and RTS frame */
	if (use_rts || use_cts) {
		if (use_rts && use_cts)
			use_cts = false;

		for (k = 0; k < 2; k++) {
			rts_rspec[k] = wlc_rspec_to_rts_rspec(wlc, rspec[k],
							      false,
							      mimo_ctlchbw);
		}

		if (!IS_OFDM(rts_rspec[0]) &&
		    !((RSPEC2RATE(rts_rspec[0]) == WLC_RATE_1M) ||
		      (wlc->PLCPHdr_override == WLC_PLCP_LONG))) {
			rts_preamble_type[0] = WLC_SHORT_PREAMBLE;
			mch |= TXC_PREAMBLE_RTS_MAIN_SHORT;
		}

		if (!IS_OFDM(rts_rspec[1]) &&
		    !((RSPEC2RATE(rts_rspec[1]) == WLC_RATE_1M) ||
		      (wlc->PLCPHdr_override == WLC_PLCP_LONG))) {
			rts_preamble_type[1] = WLC_SHORT_PREAMBLE;
			mch |= TXC_PREAMBLE_RTS_FB_SHORT;
		}

		/* RTS/CTS additions to MacTxControlLow */
		if (use_cts) {
			txh->MacTxControlLow |= htol16(TXC_SENDCTS);
		} else {
			txh->MacTxControlLow |= htol16(TXC_SENDRTS);
			txh->MacTxControlLow |= htol16(TXC_LONGFRAME);
		}

		/* RTS PLCP header */
		ASSERT(IS_ALIGNED((unsigned long)txh->RTSPhyHeader, sizeof(u16)));
		rts_plcp = txh->RTSPhyHeader;
		if (use_cts)
			rts_phylen = DOT11_CTS_LEN + DOT11_FCS_LEN;
		else
			rts_phylen = DOT11_RTS_LEN + DOT11_FCS_LEN;

		wlc_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp);

		/* fallback rate version of RTS PLCP header */
		wlc_compute_plcp(wlc, rts_rspec[1], rts_phylen,
				 rts_plcp_fallback);
		bcopy(rts_plcp_fallback, (char *)&txh->RTSPLCPFallback,
		      sizeof(txh->RTSPLCPFallback));

		/* RTS frame fields... */
		rts = (struct dot11_rts_frame *)&txh->rts_frame;

		durid = wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec[0],
					       rspec[0], rts_preamble_type[0],
					       preamble_type[0], phylen, false);
		rts->durid = htol16(durid);
		/* fallback rate version of RTS DUR field */
		durid = wlc_compute_rtscts_dur(wlc, use_cts,
					       rts_rspec[1], rspec[1],
					       rts_preamble_type[1],
					       preamble_type[1], phylen, false);
		txh->RTSDurFallback = htol16(durid);

		if (use_cts) {
			rts->fc = htol16(FC_CTS);
			bcopy((char *)&h->a2, (char *)&rts->ra, ETHER_ADDR_LEN);
		} else {
			rts->fc = htol16((u16) FC_RTS);
			bcopy((char *)&h->a1, (char *)&rts->ra,
			      2 * ETHER_ADDR_LEN);
		}

		/* mainrate
		 *    low 8 bits: main frag rate/mcs,
		 *    high 8 bits: rts/cts rate/mcs
		 */
		mainrates |= (IS_OFDM(rts_rspec[0]) ?
			      D11A_PHY_HDR_GRATE((ofdm_phy_hdr_t *) rts_plcp) :
			      rts_plcp[0]) << 8;
	} else {
		bzero((char *)txh->RTSPhyHeader, D11_PHY_HDR_LEN);
		bzero((char *)&txh->rts_frame, sizeof(struct dot11_rts_frame));
		bzero((char *)txh->RTSPLCPFallback,
		      sizeof(txh->RTSPLCPFallback));
		txh->RTSDurFallback = 0;
	}

#ifdef SUPPORT_40MHZ
	/* add null delimiter count */
	if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && IS_MCS(rspec)) {
		txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] =
		    wlc_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen);
	}
#endif

	/* Now that RTS/RTS FB preamble types are updated, write the final value */
	txh->MacTxControlHigh = htol16(mch);

	/* MainRates (both the rts and frag plcp rates have been calculated now) */
	txh->MainRates = htol16(mainrates);

	/* XtraFrameTypes */
	xfts = FRAMETYPE(rspec[1], wlc->mimoft);
	xfts |= (FRAMETYPE(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT);
	xfts |= (FRAMETYPE(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT);
	xfts |=
	    CHSPEC_CHANNEL(WLC_BAND_PI_RADIO_CHANSPEC) << XFTS_CHANNEL_SHIFT;
	txh->XtraFrameTypes = htol16(xfts);

	/* PhyTxControlWord */
	phyctl = FRAMETYPE(rspec[0], wlc->mimoft);
	if ((preamble_type[0] == WLC_SHORT_PREAMBLE) ||
	    (preamble_type[0] == WLC_GF_PREAMBLE)) {
		ASSERT((preamble_type[0] == WLC_GF_PREAMBLE)
		       || !IS_MCS(rspec[0]));
		if (RSPEC2RATE(rspec[0]) != WLC_RATE_1M)
			phyctl |= PHY_TXC_SHORT_HDR;
		WLCNTINCR(wlc->pub->_cnt->txprshort);
	}

	/* phytxant is properly bit shifted */
	phyctl |= wlc_stf_d11hdrs_phyctl_txant(wlc, rspec[0]);
	txh->PhyTxControlWord = htol16(phyctl);

	/* PhyTxControlWord_1 */
	if (WLC_PHY_11N_CAP(wlc->band)) {
		u16 phyctl1 = 0;

		phyctl1 = wlc_phytxctl1_calc(wlc, rspec[0]);
		txh->PhyTxControlWord_1 = htol16(phyctl1);
		phyctl1 = wlc_phytxctl1_calc(wlc, rspec[1]);
		txh->PhyTxControlWord_1_Fbr = htol16(phyctl1);

		if (use_rts || use_cts) {
			phyctl1 = wlc_phytxctl1_calc(wlc, rts_rspec[0]);
			txh->PhyTxControlWord_1_Rts = htol16(phyctl1);
			phyctl1 = wlc_phytxctl1_calc(wlc, rts_rspec[1]);
			txh->PhyTxControlWord_1_FbrRts = htol16(phyctl1);
		}

		/*
		 * For mcs frames, if mixedmode(overloaded with long preamble) is going to be set,
		 * fill in non-zero MModeLen and/or MModeFbrLen
		 *  it will be unnecessary if they are separated
		 */
		if (IS_MCS(rspec[0]) && (preamble_type[0] == WLC_MM_PREAMBLE)) {
			u16 mmodelen =
			    wlc_calc_lsig_len(wlc, rspec[0], phylen);
			txh->MModeLen = htol16(mmodelen);
		}

		if (IS_MCS(rspec[1]) && (preamble_type[1] == WLC_MM_PREAMBLE)) {
			u16 mmodefbrlen =
			    wlc_calc_lsig_len(wlc, rspec[1], phylen);
			txh->MModeFbrLen = htol16(mmodefbrlen);
		}
	}

	if (IS_MCS(rspec[0]))
		ASSERT(IS_MCS(rspec[1]));

	ASSERT(!IS_MCS(rspec[0]) ||
	       ((preamble_type[0] == WLC_MM_PREAMBLE) == (txh->MModeLen != 0)));
	ASSERT(!IS_MCS(rspec[1]) ||
	       ((preamble_type[1] == WLC_MM_PREAMBLE) ==
		(txh->MModeFbrLen != 0)));

	ac = wme_fifo2ac[queue];
	if (SCB_WME(scb) && qos && wlc->edcf_txop[ac]) {
		uint frag_dur, dur, dur_fallback;

		ASSERT(!ETHER_ISMULTI(&h->a1));

		/* WME: Update TXOP threshold */
		if ((!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) && (frag == 0)) {
			frag_dur =
			    wlc_calc_frame_time(wlc, rspec[0], preamble_type[0],
						phylen);

			if (rts) {
				/* 1 RTS or CTS-to-self frame */
				dur =
				    wlc_calc_cts_time(wlc, rts_rspec[0],
						      rts_preamble_type[0]);
				dur_fallback =
				    wlc_calc_cts_time(wlc, rts_rspec[1],
						      rts_preamble_type[1]);
				/* (SIFS + CTS) + SIFS + frame + SIFS + ACK */
				dur += ltoh16(rts->durid);
				dur_fallback += ltoh16(txh->RTSDurFallback);
			} else if (use_rifs) {
				dur = frag_dur;
				dur_fallback = 0;
			} else {
				/* frame + SIFS + ACK */
				dur = frag_dur;
				dur +=
				    wlc_compute_frame_dur(wlc, rspec[0],
							  preamble_type[0], 0);

				dur_fallback =
				    wlc_calc_frame_time(wlc, rspec[1],
							preamble_type[1],
							phylen);
				dur_fallback +=
				    wlc_compute_frame_dur(wlc, rspec[1],
							  preamble_type[1], 0);
			}
			/* NEED to set TxFesTimeNormal (hard) */
			txh->TxFesTimeNormal = htol16((u16) dur);
			/* NEED to set fallback rate version of TxFesTimeNormal (hard) */
			txh->TxFesTimeFallback = htol16((u16) dur_fallback);

			/* update txop byte threshold (txop minus intraframe overhead) */
			if (wlc->edcf_txop[ac] >= (dur - frag_dur)) {
				{
					uint newfragthresh;

					newfragthresh =
					    wlc_calc_frame_len(wlc, rspec[0],
							       preamble_type[0],
							       (wlc->
								edcf_txop[ac] -
								(dur -
								 frag_dur)));
					/* range bound the fragthreshold */
					if (newfragthresh < DOT11_MIN_FRAG_LEN)
						newfragthresh =
						    DOT11_MIN_FRAG_LEN;
					else if (newfragthresh >
						 wlc->usr_fragthresh)
						newfragthresh =
						    wlc->usr_fragthresh;
					/* update the fragthresh and do txc update */
					if (wlc->fragthresh[queue] !=
					    (u16) newfragthresh) {
						wlc->fragthresh[queue] =
						    (u16) newfragthresh;
					}
				}
			} else
				WL_ERROR(("wl%d: %s txop invalid for rate %d\n",
					  wlc->pub->unit, fifo_names[queue],
					  RSPEC2RATE(rspec[0])));

			if (dur > wlc->edcf_txop[ac])
				WL_ERROR(("wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n", wlc->pub->unit, __func__, fifo_names[queue], phylen, wlc->fragthresh[queue], dur, wlc->edcf_txop[ac]));
		}
	}

	return 0;
}

Generated by GNU enscript 1.6.4.