svn commit: r252727 - in head/sys: dev/iwn dev/ral net80211

Adrian Chadd adrian at FreeBSD.org
Thu Jul 4 21:16:51 UTC 2013


Author: adrian
Date: Thu Jul  4 21:16:49 2013
New Revision: 252727
URL: http://svnweb.freebsd.org/changeset/base/252727

Log:
  Implement basic 802.11n awareness in the PHY and AMRR rate control code.
  
  * Add 802.11n 2ghz and 5ghz tables, including legacy rates and up to
    MCS23 rates (3x3.)
  
  * Populate the rate code -> rate index lookup table with MCS _and_
    normal rates, but _not_ the basic rate flag.  Since the basic rate flag
    is the same as the MCS flag, we can only use one.
  
  * Introduce some accessor inlines that do PLCP and rate table lookup/access
    and enforce that it doesn't set the basic rate bit.  They're not
    designed for MCS rates, so it will panic.
  
  * Start converting drivers that use the rate table stuff to use the
    accessor inlines and strip the basic flag.
  
  * Teach AMRR about basic 11n - it's still as crap for MCS as it is
    being used by iwn, so it's not a step _backwardS_.
  
  * Convert iwn over to accept 11n MCS rates rather than 'translate' legacy
    to MCS rates.  It doesn't use a lookup table any longer; instead it's a
    function which takes the current node (for HT parameters) and the
    rate code, and returns the hardware PLCP code to use.
  
  Tested:
  
  * ath - it's a no-op, and it works that way
  * iwn - both 11n and non-11n

Modified:
  head/sys/dev/iwn/if_iwn.c
  head/sys/dev/iwn/if_iwnvar.h
  head/sys/dev/ral/rt2560.c
  head/sys/dev/ral/rt2661.c
  head/sys/dev/ral/rt2860.c
  head/sys/net80211/ieee80211_amrr.c
  head/sys/net80211/ieee80211_phy.c
  head/sys/net80211/ieee80211_phy.h

Modified: head/sys/dev/iwn/if_iwn.c
==============================================================================
--- head/sys/dev/iwn/if_iwn.c	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/dev/iwn/if_iwn.c	Thu Jul  4 21:16:49 2013	(r252727)
@@ -335,6 +335,8 @@ enum {
 	IWN_DEBUG_NODE		= 0x00000400,	/* node management */
 	IWN_DEBUG_LED		= 0x00000800,	/* led management */
 	IWN_DEBUG_CMD		= 0x00001000,	/* cmd submission */
+	IWN_DEBUG_TXRATE	= 0x00002000,	/* TX rate debugging */
+	IWN_DEBUG_PWRSAVE	= 0x00004000,	/* Power save operations */
 	IWN_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
 	IWN_DEBUG_ANY		= 0xffffffff
 };
@@ -2098,56 +2100,106 @@ rate2plcp(int rate)
 	return 0;
 }
 
-static void
-iwn_newassoc(struct ieee80211_node *ni, int isnew)
+/*
+ * Calculate the required PLCP value from the given rate,
+ * to the given node.
+ *
+ * This will take the node configuration (eg 11n, rate table
+ * setup, etc) into consideration.
+ */
+static uint32_t
+iwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni,
+    uint8_t rate)
 {
 #define	RV(v)	((v) & IEEE80211_RATE_VAL)
 	struct ieee80211com *ic = ni->ni_ic;
-	struct iwn_softc *sc = ic->ic_ifp->if_softc;
-	struct iwn_node *wn = (void *)ni;
 	uint8_t txant1, txant2;
-	int i, plcp, rate, ridx;
+	uint32_t plcp = 0;
+	int ridx;
 
 	/* Use the first valid TX antenna. */
 	txant1 = IWN_LSB(sc->txchainmask);
 	txant2 = IWN_LSB(sc->txchainmask & ~txant1);
 
+	/*
+	 * If it's an MCS rate, let's set the plcp correctly
+	 * and set the relevant flags based on the node config.
+	 */
 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
-		ridx = ni->ni_rates.rs_nrates - 1;
-		for (i = ni->ni_htrates.rs_nrates - 1; i >= 0; i--) {
-			plcp = RV(ni->ni_htrates.rs_rates[i]) | IWN_RFLAG_MCS;
-			if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
-				plcp |= IWN_RFLAG_HT40;
-				if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
-					plcp |= IWN_RFLAG_SGI;
-			} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+		/*
+		 * Set the initial PLCP value to be between 0->31 for
+		 * MCS 0 -> MCS 31, then set the "I'm an MCS rate!"
+		 * flag.
+		 */
+		plcp = RV(rate) | IWN_RFLAG_MCS;
+
+		/*
+		 * XXX the following should only occur if both
+		 * the local configuration _and_ the remote node
+		 * advertise these capabilities.  Thus this code
+		 * may need fixing!
+		 */
+
+		/*
+		 * Set the channel width and guard interval.
+		 */
+		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+			plcp |= IWN_RFLAG_HT40;
+			if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
 				plcp |= IWN_RFLAG_SGI;
-			if (RV(ni->ni_htrates.rs_rates[i]) > 7)
-				plcp |= IWN_RFLAG_ANT(txant1 | txant2);
-			else
-				plcp |= IWN_RFLAG_ANT(txant1);
-			if (ridx >= 0) {
-				rate = RV(ni->ni_rates.rs_rates[ridx]);
-				wn->ridx[rate] = plcp;
-			}
-			wn->ridx[IEEE80211_RATE_MCS | i] = plcp;
-			ridx--;
+		} else if (ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) {
+			plcp |= IWN_RFLAG_SGI;
 		}
-	} else {
-		for (i = 0; i < ni->ni_rates.rs_nrates; i++) {
-			rate = RV(ni->ni_rates.rs_rates[i]);
-			plcp = rate2plcp(rate);
-			ridx = ic->ic_rt->rateCodeToIndex[rate];
-			if (ridx < IWN_RIDX_OFDM6 &&
-			    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
-				plcp |= IWN_RFLAG_CCK;
+
+		/*
+		 * If it's a two stream rate, enable TX on both
+		 * antennas.
+		 *
+		 * XXX three stream rates?
+		 */
+		if (rate > 0x87)
+			plcp |= IWN_RFLAG_ANT(txant1 | txant2);
+		else
 			plcp |= IWN_RFLAG_ANT(txant1);
-			wn->ridx[rate] = htole32(plcp);
-		}
-	}
+	} else {
+		/*
+		 * Set the initial PLCP - fine for both
+		 * OFDM and CCK rates.
+		 */
+		plcp = rate2plcp(rate);
+
+		/* Set CCK flag if it's CCK */
+
+		/* XXX It would be nice to have a method
+		 * to map the ridx -> phy table entry
+		 * so we could just query that, rather than
+		 * this hack to check against IWN_RIDX_OFDM6.
+		 */
+		ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+		    rate & IEEE80211_RATE_VAL);
+		if (ridx < IWN_RIDX_OFDM6 &&
+		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+			plcp |= IWN_RFLAG_CCK;
+
+		/* Set antenna configuration */
+		plcp |= IWN_RFLAG_ANT(txant1);
+	}
+
+	DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n",
+	    __func__,
+	    rate,
+	    plcp);
+
+	return (htole32(plcp));
 #undef	RV
 }
 
+static void
+iwn_newassoc(struct ieee80211_node *ni, int isnew)
+{
+	/* Doesn't do anything at the moment */
+}
+
 static int
 iwn_media_change(struct ifnet *ifp)
 {
@@ -3401,7 +3453,8 @@ iwn_tx_data(struct iwn_softc *sc, struct
 		(void) ieee80211_ratectl_rate(ni, NULL, 0);
 		rate = ni->ni_txrate;
 	}
-	ridx = ic->ic_rt->rateCodeToIndex[rate];
+	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+	    rate & IEEE80211_RATE_VAL);
 
 	/* Encrypt the frame if need be. */
 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
@@ -3507,7 +3560,7 @@ iwn_tx_data(struct iwn_softc *sc, struct
 	tx->rts_ntries = 60;
 	tx->data_ntries = 15;
 	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
-	tx->rate = wn->ridx[rate];
+	tx->rate = iwn_rate_to_plcp(sc, ni, rate);
 	if (tx->id == sc->broadcast_id) {
 		/* Group or management frame. */
 		tx->linkq = 0;
@@ -3638,7 +3691,8 @@ iwn_tx_data_raw(struct iwn_softc *sc, st
 
 	/* Choose a TX rate index. */
 	rate = params->ibp_rate0;
-	ridx = ic->ic_rt->rateCodeToIndex[rate];
+	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+	    rate & IEEE80211_RATE_VAL);
 	if (ridx == (uint8_t)-1) {
 		/* XXX fall back to mcast/mgmt rate? */
 		m_freem(m);
@@ -3714,14 +3768,18 @@ iwn_tx_data_raw(struct iwn_softc *sc, st
 	tx->rts_ntries = params->ibp_try1;
 	tx->data_ntries = params->ibp_try0;
 	tx->lifetime = htole32(IWN_LIFETIME_INFINITE);
+
+	/* XXX should just use  iwn_rate_to_plcp() */
 	tx->rate = htole32(rate2plcp(rate));
 	if (ridx < IWN_RIDX_OFDM6 &&
 	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
 		tx->rate |= htole32(IWN_RFLAG_CCK);
+
 	/* Group or management frame. */
 	tx->linkq = 0;
 	txant = IWN_LSB(sc->txchainmask);
 	tx->rate |= htole32(IWN_RFLAG_ANT(txant));
+
 	/* Set physical address of "scratch area". */
 	tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr));
 	tx->hiaddr = IWN_HIADDR(data->scratch_paddr);
@@ -4077,14 +4135,20 @@ iwn_set_link_quality(struct iwn_softc *s
 	else
 		txrate = rs->rs_nrates - 1;
 	for (i = 0; i < IWN_MAX_TX_RETRIES; i++) {
+		uint32_t plcp;
+
 		if (IEEE80211_IS_CHAN_HT(ni->ni_chan))
 			rate = IEEE80211_RATE_MCS | txrate;
 		else
 			rate = RV(rs->rs_rates[txrate]);
-		linkq.retry[i] = wn->ridx[rate];
 
-		if ((le32toh(wn->ridx[rate]) & IWN_RFLAG_MCS) &&
-		    RV(le32toh(wn->ridx[rate])) > 7)
+		/* Do rate -> PLCP config mapping */
+		plcp = iwn_rate_to_plcp(sc, ni, rate);
+		linkq.retry[i] = plcp;
+
+		/* Special case for dual-stream rates? */
+		if ((le32toh(plcp) & IWN_RFLAG_MCS) &&
+		    RV(le32toh(plcp)) > 7)
 			linkq.mimo = i + 1;
 
 		/* Next retry at immediate lower bit-rate. */
@@ -4940,6 +5004,13 @@ iwn_set_pslevel(struct iwn_softc *sc, in
 	uint32_t reg;
 	int i;
 
+	DPRINTF(sc, IWN_DEBUG_PWRSAVE,
+	    "%s: dtim=%d, level=%d, async=%d\n",
+	    __func__,
+	    dtim,
+	    level,
+	    async);
+
 	/* Select which PS parameters to use. */
 	if (dtim <= 2)
 		pmgt = &iwn_pmgt[0][level];

Modified: head/sys/dev/iwn/if_iwnvar.h
==============================================================================
--- head/sys/dev/iwn/if_iwnvar.h	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/dev/iwn/if_iwnvar.h	Thu Jul  4 21:16:49 2013	(r252727)
@@ -102,7 +102,6 @@ struct iwn_node {
 	struct	ieee80211_node		ni;	/* must be the first */
 	uint16_t			disable_tid;
 	uint8_t				id;
-	uint32_t			ridx[256];
 	struct {
 		uint64_t		bitmap;
 		int			startidx;

Modified: head/sys/dev/ral/rt2560.c
==============================================================================
--- head/sys/dev/ral/rt2560.c	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/dev/ral/rt2560.c	Thu Jul  4 21:16:49 2013	(r252727)
@@ -2370,7 +2370,7 @@ rt2560_set_basicrates(struct rt2560_soft
 		if (!(rate & IEEE80211_RATE_BASIC))
 			continue;
 
-		mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)];
+		mask |= 1 << ieee80211_legacy_rate_lookup(ic->ic_rt, RV(rate));
 	}
 
 	RAL_WRITE(sc, RT2560_ARSP_PLCP_1, mask);

Modified: head/sys/dev/ral/rt2661.c
==============================================================================
--- head/sys/dev/ral/rt2661.c	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/dev/ral/rt2661.c	Thu Jul  4 21:16:49 2013	(r252727)
@@ -1923,7 +1923,7 @@ rt2661_set_basicrates(struct rt2661_soft
 		if (!(rate & IEEE80211_RATE_BASIC))
 			continue;
 
-		mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)];
+		mask |= 1 << ieee80211_legacy_rate_lookup(ic->ic_rt, RV(rate));
 	}
 
 	RAL_WRITE(sc, RT2661_TXRX_CSR5, mask);

Modified: head/sys/dev/ral/rt2860.c
==============================================================================
--- head/sys/dev/ral/rt2860.c	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/dev/ral/rt2860.c	Thu Jul  4 21:16:49 2013	(r252727)
@@ -1528,7 +1528,7 @@ rt2860_tx(struct rt2860_softc *sc, struc
 		tid = 0;
 	}
 	ring = &sc->txq[qid];
-	ridx = ic->ic_rt->rateCodeToIndex[rate];
+	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt, rate);
 
 	/* get MCS code from rate index */
 	mcs = rt2860_rates[ridx].mcs;
@@ -1779,7 +1779,8 @@ rt2860_tx_raw(struct rt2860_softc *sc, s
 
 	/* Choose a TX rate index. */
 	rate = params->ibp_rate0;
-	ridx = ic->ic_rt->rateCodeToIndex[rate];
+	ridx = ieee80211_legacy_rate_lookup(ic->ic_rt,
+	    rate & IEEE80211_RATE_VAL);
 	if (ridx == (uint8_t)-1) {
 		/* XXX fall back to mcast/mgmt rate? */
 		m_freem(m);
@@ -2311,7 +2312,7 @@ rt2860_set_basicrates(struct rt2860_soft
 		if (!(rate & IEEE80211_RATE_BASIC))
 			continue;
 
-		mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)];
+		mask |= 1 << ieee80211_legacy_rate_lookup(ic->ic_rt, RV(rate));
 	}
 
 	RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, mask);

Modified: head/sys/net80211/ieee80211_amrr.c
==============================================================================
--- head/sys/net80211/ieee80211_amrr.c	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/net80211/ieee80211_amrr.c	Thu Jul  4 21:16:49 2013	(r252727)
@@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$");
 #endif
 
 #include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ht.h>
 #include <net80211/ieee80211_amrr.h>
 #include <net80211/ieee80211_ratectl.h>
 
@@ -128,13 +129,25 @@ amrr_deinit(struct ieee80211vap *vap)
 	free(vap->iv_rs, M_80211_RATECTL);
 }
 
+static int
+amrr_node_is_11n(struct ieee80211_node *ni)
+{
+
+	if (ni->ni_chan == NULL)
+		return (0);
+	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+		return (0);
+	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
+}
+
 static void
 amrr_node_init(struct ieee80211_node *ni)
 {
-	const struct ieee80211_rateset *rs = &ni->ni_rates;
+	const struct ieee80211_rateset *rs = NULL;
 	struct ieee80211vap *vap = ni->ni_vap;
 	struct ieee80211_amrr *amrr = vap->iv_rs;
 	struct ieee80211_amrr_node *amn;
+	uint8_t rate;
 
 	if (ni->ni_rctls == NULL) {
 		ni->ni_rctls = amn = malloc(sizeof(struct ieee80211_amrr_node),
@@ -152,16 +165,50 @@ amrr_node_init(struct ieee80211_node *ni
 	amn->amn_txcnt = amn->amn_retrycnt = 0;
 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
 
-	/* pick initial rate */
-	for (amn->amn_rix = rs->rs_nrates - 1;
-	     amn->amn_rix > 0 && (rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) > 72;
-	     amn->amn_rix--)
-		;
-	ni->ni_txrate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+	/* 11n or not? Pick the right rateset */
+	if (amrr_node_is_11n(ni)) {
+		/* XXX ew */
+		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+		    "%s: 11n node", __func__);
+		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+	} else {
+		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+		    "%s: non-11n node", __func__);
+		rs = &ni->ni_rates;
+	}
+
+	/* Initial rate - lowest */
+	rate = rs->rs_rates[0];
+
+	/* XXX clear the basic rate flag if it's not 11n */
+	if (! amrr_node_is_11n(ni))
+		rate &= IEEE80211_RATE_VAL;
+
+	/* pick initial rate from the rateset - HT or otherwise */
+	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
+	    amn->amn_rix--) {
+		/* legacy - anything < 36mbit, stop searching */
+		/* 11n - stop at MCS4 / MCS12 / MCS28 */
+		if (amrr_node_is_11n(ni) &&
+		    (rs->rs_rates[amn->amn_rix] & 0x7) < 4)
+			break;
+		else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
+			break;
+		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
+	}
+
+	/* if the rate is an 11n rate, ensure the MCS bit is set */
+	if (amrr_node_is_11n(ni))
+		rate |= IEEE80211_RATE_MCS;
+
+	/* Assign initial rate from the rateset */
+	ni->ni_txrate = rate;
 	amn->amn_ticks = ticks;
 
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-	    "AMRR initial rate %d", ni->ni_txrate);
+	    "AMRR: nrates=%d, initial rate %d",
+	    rs->rs_nrates,
+	    rate);
 }
 
 static void
@@ -175,19 +222,34 @@ amrr_update(struct ieee80211_amrr *amrr,
     struct ieee80211_node *ni)
 {
 	int rix = amn->amn_rix;
+	const struct ieee80211_rateset *rs = NULL;
 
 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
 
+	/* 11n or not? Pick the right rateset */
+	if (amrr_node_is_11n(ni)) {
+		/* XXX ew */
+		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+	} else {
+		rs = &ni->ni_rates;
+	}
+
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
+	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
+	    amn->amn_txcnt,
+	    amn->amn_retrycnt);
+
 	if (is_success(amn)) {
 		amn->amn_success++;
 		if (amn->amn_success >= amn->amn_success_threshold &&
-		    rix + 1 < ni->ni_rates.rs_nrates) {
+		    rix + 1 < rs->rs_nrates) {
 			amn->amn_recovery = 1;
 			amn->amn_success = 0;
 			rix++;
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
 			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
-			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
+			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 			    amn->amn_txcnt, amn->amn_retrycnt);
 		} else {
 			amn->amn_recovery = 0;
@@ -208,7 +270,7 @@ amrr_update(struct ieee80211_amrr *amrr,
 			rix--;
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
 			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
-			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL,
+			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 			    amn->amn_txcnt, amn->amn_retrycnt);
 		}
 		amn->amn_recovery = 0;
@@ -231,14 +293,27 @@ amrr_rate(struct ieee80211_node *ni, voi
 {
 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
 	struct ieee80211_amrr *amrr = amn->amn_amrr;
+	const struct ieee80211_rateset *rs = NULL;
 	int rix;
 
+	/* 11n or not? Pick the right rateset */
+	if (amrr_node_is_11n(ni)) {
+		/* XXX ew */
+		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
+	} else {
+		rs = &ni->ni_rates;
+	}
+
 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
 		rix = amrr_update(amrr, amn, ni);
 		if (rix != amn->amn_rix) {
 			/* update public rate */
-			ni->ni_txrate =
-			    ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
+			ni->ni_txrate = rs->rs_rates[rix];
+			/* XXX strip basic rate flag from txrate, if non-11n */
+			if (amrr_node_is_11n(ni))
+				ni->ni_txrate |= IEEE80211_RATE_MCS;
+			else
+				ni->ni_txrate &= IEEE80211_RATE_VAL;
 			amn->amn_rix = rix;
 		}
 		amn->amn_ticks = ticks;

Modified: head/sys/net80211/ieee80211_phy.c
==============================================================================
--- head/sys/net80211/ieee80211_phy.c	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/net80211/ieee80211_phy.c	Thu Jul  4 21:16:49 2013	(r252727)
@@ -60,8 +60,11 @@ struct ieee80211_ds_plcp_hdr {
 #define	TURBO	IEEE80211_T_TURBO
 #define	HALF	IEEE80211_T_OFDM_HALF
 #define	QUART	IEEE80211_T_OFDM_QUARTER
+#define	HT	IEEE80211_T_HT
+/* XXX the 11n and the basic rate flag are unfortunately overlapping. Grr. */
+#define	N(r)	(IEEE80211_RATE_MCS | r)
 #define	PBCC	(IEEE80211_T_OFDM_QUARTER+1)		/* XXX */
-#define	B(r)	(0x80 | r)
+#define	B(r)	(IEEE80211_RATE_BASIC | r)
 #define	Mb(x)	(x*1000)
 
 static struct ieee80211_rate_table ieee80211_11b_table = {
@@ -176,6 +179,98 @@ static struct ieee80211_rate_table ieee8
     },
 };
 
+static struct ieee80211_rate_table ieee80211_11ng_table = {
+    .rateCount = 36,
+    .info = {
+/*                                   short            ctrl  */
+/*                                Preamble  dot11Rate Rate */
+     [0] = { .phy = CCK,     1000,    0x00,      B(2),   0 },
+     [1] = { .phy = CCK,     2000,    0x04,      B(4),   1 },
+     [2] = { .phy = CCK,     5500,    0x04,     B(11),   2 },
+     [3] = { .phy = CCK,    11000,    0x04,     B(22),   3 },
+     [4] = { .phy = OFDM,    6000,    0x00,        12,   4 },
+     [5] = { .phy = OFDM,    9000,    0x00,        18,   4 },
+     [6] = { .phy = OFDM,   12000,    0x00,        24,   6 },
+     [7] = { .phy = OFDM,   18000,    0x00,        36,   6 },
+     [8] = { .phy = OFDM,   24000,    0x00,        48,   8 },
+     [9] = { .phy = OFDM,   36000,    0x00,        72,   8 },
+    [10] = { .phy = OFDM,   48000,    0x00,        96,   8 },
+    [11] = { .phy = OFDM,   54000,    0x00,       108,   8 },
+
+    [12] = { .phy = HT,      6500,    0x00,      N(0),   4 },
+    [13] = { .phy = HT,     13000,    0x00,      N(1),   6 },
+    [14] = { .phy = HT,     19500,    0x00,      N(2),   6 },
+    [15] = { .phy = HT,     26000,    0x00,      N(3),   8 },
+    [16] = { .phy = HT,     39000,    0x00,      N(4),   8 },
+    [17] = { .phy = HT,     52000,    0x00,      N(5),   8 },
+    [18] = { .phy = HT,     58500,    0x00,      N(6),   8 },
+    [19] = { .phy = HT,     65000,    0x00,      N(7),   8 },
+
+    [20] = { .phy = HT,     13000,    0x00,      N(8),   4 },
+    [21] = { .phy = HT,     26000,    0x00,      N(9),   6 },
+    [22] = { .phy = HT,     39000,    0x00,     N(10),   6 },
+    [23] = { .phy = HT,     52000,    0x00,     N(11),   8 },
+    [24] = { .phy = HT,     78000,    0x00,     N(12),   8 },
+    [25] = { .phy = HT,    104000,    0x00,     N(13),   8 },
+    [26] = { .phy = HT,    117000,    0x00,     N(14),   8 },
+    [27] = { .phy = HT,    130000,    0x00,     N(15),   8 },
+
+    [28] = { .phy = HT,     19500,    0x00,     N(16),   4 },
+    [29] = { .phy = HT,     39000,    0x00,     N(17),   6 },
+    [30] = { .phy = HT,     58500,    0x00,     N(18),   6 },
+    [31] = { .phy = HT,     78000,    0x00,     N(19),   8 },
+    [32] = { .phy = HT,    117000,    0x00,     N(20),   8 },
+    [33] = { .phy = HT,    156000,    0x00,     N(21),   8 },
+    [34] = { .phy = HT,    175500,    0x00,     N(22),   8 },
+    [35] = { .phy = HT,    195000,    0x00,     N(23),   8 },
+
+    },
+};
+
+static struct ieee80211_rate_table ieee80211_11na_table = {
+    .rateCount = 32,
+    .info = {
+/*                                   short            ctrl  */
+/*                                Preamble  dot11Rate Rate */
+     [0] = { .phy = OFDM,    6000,    0x00,     B(12),   0 },
+     [1] = { .phy = OFDM,    9000,    0x00,        18,   0 },
+     [2] = { .phy = OFDM,   12000,    0x00,     B(24),   2 },
+     [3] = { .phy = OFDM,   18000,    0x00,        36,   2 },
+     [4] = { .phy = OFDM,   24000,    0x00,     B(48),   4 },
+     [5] = { .phy = OFDM,   36000,    0x00,        72,   4 },
+     [6] = { .phy = OFDM,   48000,    0x00,        96,   4 },
+     [7] = { .phy = OFDM,   54000,    0x00,       108,   4 },
+
+     [8] = { .phy = HT,      6500,    0x00,      N(0),   0 },
+     [9] = { .phy = HT,     13000,    0x00,      N(1),   2 },
+    [10] = { .phy = HT,     19500,    0x00,      N(2),   2 },
+    [11] = { .phy = HT,     26000,    0x00,      N(3),   4 },
+    [12] = { .phy = HT,     39000,    0x00,      N(4),   4 },
+    [13] = { .phy = HT,     52000,    0x00,      N(5),   4 },
+    [14] = { .phy = HT,     58500,    0x00,      N(6),   4 },
+    [15] = { .phy = HT,     65000,    0x00,      N(7),   4 },
+
+    [16] = { .phy = HT,     13000,    0x00,      N(8),   0 },
+    [17] = { .phy = HT,     26000,    0x00,      N(9),   2 },
+    [18] = { .phy = HT,     39000,    0x00,     N(10),   2 },
+    [19] = { .phy = HT,     52000,    0x00,     N(11),   4 },
+    [20] = { .phy = HT,     78000,    0x00,     N(12),   4 },
+    [21] = { .phy = HT,    104000,    0x00,     N(13),   4 },
+    [22] = { .phy = HT,    117000,    0x00,     N(14),   4 },
+    [23] = { .phy = HT,    130000,    0x00,     N(15),   4 },
+
+    [24] = { .phy = HT,     19500,    0x00,     N(16),   0 },
+    [25] = { .phy = HT,     39000,    0x00,     N(17),   2 },
+    [26] = { .phy = HT,     58500,    0x00,     N(18),   2 },
+    [27] = { .phy = HT,     78000,    0x00,     N(19),   4 },
+    [28] = { .phy = HT,    117000,    0x00,     N(20),   4 },
+    [29] = { .phy = HT,    156000,    0x00,     N(21),   4 },
+    [30] = { .phy = HT,    175500,    0x00,     N(22),   4 },
+    [31] = { .phy = HT,    195000,    0x00,     N(23),   4 },
+
+    },
+};
+
 #undef	Mb
 #undef	B
 #undef	OFDM
@@ -184,6 +279,8 @@ static struct ieee80211_rate_table ieee8
 #undef	CCK
 #undef	TURBO
 #undef	XR
+#undef	HT
+#undef	N
 
 /*
  * Setup a rate table's reverse lookup table and fill in
@@ -210,15 +307,23 @@ ieee80211_setup_ratetable(struct ieee802
 		uint8_t cix = rt->info[i].ctlRateIndex;
 		uint8_t ctl_rate = rt->info[cix].dot11Rate;
 
-		rt->rateCodeToIndex[code] = i;
-		if (code & IEEE80211_RATE_BASIC) {
-			/*
-			 * Map w/o basic rate bit too.
-			 */
-			code &= IEEE80211_RATE_VAL;
-			rt->rateCodeToIndex[code] = i;
+		/*
+		 * Map without the basic rate bit.
+		 *
+		 * It's up to the caller to ensure that the basic
+		 * rate bit is stripped here.
+		 *
+		 * For HT, use the MCS rate bit.
+		 */
+		code &= IEEE80211_RATE_VAL;
+		if (rt->info[i].phy == IEEE80211_T_HT) {
+			code |= IEEE80211_RATE_MCS;
 		}
 
+		/* XXX assume the control rate is non-MCS? */
+		ctl_rate &= IEEE80211_RATE_VAL;
+		rt->rateCodeToIndex[code] = i;
+
 		/*
 		 * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
 		 *     depends on whether they are marked as basic rates;
@@ -247,11 +352,10 @@ ieee80211_phy_init(void)
 	static struct ieee80211_rate_table * const ratetables[] = {
 		&ieee80211_half_table,
 		&ieee80211_quarter_table,
-		&ieee80211_11a_table,
-		&ieee80211_11g_table,
+		&ieee80211_11na_table,
+		&ieee80211_11ng_table,
 		&ieee80211_turbog_table,
 		&ieee80211_turboa_table,
-		&ieee80211_turboa_table,
 		&ieee80211_11a_table,
 		&ieee80211_11g_table,
 		&ieee80211_11b_table
@@ -276,9 +380,9 @@ ieee80211_get_ratetable(struct ieee80211
 	else if (IEEE80211_IS_CHAN_QUARTER(c))
 		rt = &ieee80211_quarter_table;
 	else if (IEEE80211_IS_CHAN_HTA(c))
-		rt = &ieee80211_11a_table;	/* XXX */
+		rt = &ieee80211_11na_table;
 	else if (IEEE80211_IS_CHAN_HTG(c))
-		rt = &ieee80211_11g_table;	/* XXX */
+		rt = &ieee80211_11ng_table;
 	else if (IEEE80211_IS_CHAN_108G(c))
 		rt = &ieee80211_turbog_table;
 	else if (IEEE80211_IS_CHAN_ST(c))
@@ -463,3 +567,66 @@ ieee80211_compute_duration(const struct 
 	}
 	return txTime;
 }
+
+static const uint16_t ht20_bps[32] = {
+	26, 52, 78, 104, 156, 208, 234, 260,
+	52, 104, 156, 208, 312, 416, 468, 520,
+	78, 156, 234, 312, 468, 624, 702, 780,
+	104, 208, 312, 416, 624, 832, 936, 1040
+};
+static const uint16_t ht40_bps[32] = {
+	54, 108, 162, 216, 324, 432, 486, 540,
+	108, 216, 324, 432, 648, 864, 972, 1080,
+	162, 324, 486, 648, 972, 1296, 1458, 1620,
+	216, 432, 648, 864, 1296, 1728, 1944, 2160
+};
+
+
+#define	OFDM_PLCP_BITS	22
+#define	HT_L_STF	8
+#define	HT_L_LTF	8
+#define	HT_L_SIG	4
+#define	HT_SIG		8
+#define	HT_STF		4
+#define	HT_LTF(n)	((n) * 4)
+
+#define	HT_RC_2_MCS(_rc)	((_rc) & 0xf)
+#define	HT_RC_2_STREAMS(_rc)	((((_rc) & 0x78) >> 3) + 1)
+#define	IS_HT_RATE(_rc)		( (_rc) & IEEE80211_RATE_MCS)
+
+/*
+ * Calculate the transmit duration of an 11n frame.
+ */
+uint32_t
+ieee80211_compute_duration_ht(uint32_t frameLen, uint16_t rate,
+    int streams, int isht40, int isShortGI)
+{
+	uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
+
+	KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
+	KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
+
+	if (isht40)
+		bitsPerSymbol = ht40_bps[rate & 0x1f];
+	else
+		bitsPerSymbol = ht20_bps[rate & 0x1f];
+	numBits = OFDM_PLCP_BITS + (frameLen << 3);
+	numSymbols = howmany(numBits, bitsPerSymbol);
+	if (isShortGI)
+		txTime = ((numSymbols * 18) + 4) / 5;   /* 3.6us */
+	else
+		txTime = numSymbols * 4;                /* 4us */
+	return txTime + HT_L_STF + HT_L_LTF +
+	    HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
+}
+
+#undef	IS_HT_RATE
+#undef	HT_RC_2_STREAMS
+#undef	HT_RC_2_MCS
+#undef	HT_LTF
+#undef	HT_STF
+#undef	HT_SIG
+#undef	HT_L_SIG
+#undef	HT_L_LTF
+#undef	HT_L_STF
+#undef	OFDM_PLCP_BITS

Modified: head/sys/net80211/ieee80211_phy.h
==============================================================================
--- head/sys/net80211/ieee80211_phy.h	Thu Jul  4 21:12:58 2013	(r252726)
+++ head/sys/net80211/ieee80211_phy.h	Thu Jul  4 21:16:49 2013	(r252727)
@@ -60,6 +60,8 @@
 
 struct ieee80211_channel;
 
+#define	IEEE80211_RATE_TABLE_SIZE	128
+
 struct ieee80211_rate_table {
 	int		rateCount;		/* NB: for proper padding */
 	uint8_t		rateCodeToIndex[256];	/* back mapping */
@@ -74,7 +76,7 @@ struct ieee80211_rate_table {
 						 * rate; used for dur. calcs */
 		uint16_t	lpAckDuration;	/* long preamble ACK dur. */
 		uint16_t	spAckDuration;	/* short preamble ACK dur. */
-	} info[32];
+	} info[IEEE80211_RATE_TABLE_SIZE];
 };
 
 const struct ieee80211_rate_table *ieee80211_get_ratetable(
@@ -83,7 +85,14 @@ const struct ieee80211_rate_table *ieee8
 static __inline__ uint8_t
 ieee80211_ack_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
-	uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+	uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
 	KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
 	return rt->info[cix].dot11Rate;
 }
@@ -91,7 +100,14 @@ ieee80211_ack_rate(const struct ieee8021
 static __inline__ uint8_t
 ieee80211_ctl_rate(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
-	uint8_t cix = rt->info[rt->rateCodeToIndex[rate]].ctlRateIndex;
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+	uint8_t cix = rt->info[rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]].ctlRateIndex;
 	KASSERT(cix != (uint8_t)-1, ("rate %d has no info", rate));
 	return rt->info[cix].dot11Rate;
 }
@@ -99,7 +115,14 @@ ieee80211_ctl_rate(const struct ieee8021
 static __inline__ enum ieee80211_phytype
 ieee80211_rate2phytype(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
-	uint8_t rix = rt->rateCodeToIndex[rate];
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
+	uint8_t rix = rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL];
 	KASSERT(rix != (uint8_t)-1, ("rate %d has no info", rate));
 	return rt->info[rix].phy;
 }
@@ -107,6 +130,13 @@ ieee80211_rate2phytype(const struct ieee
 static __inline__ int
 ieee80211_isratevalid(const struct ieee80211_rate_table *rt, uint8_t rate)
 {
+	/*
+	 * XXX Assert this is for a legacy rate; not for an MCS rate.
+	 * If the caller wishes to use it for a basic rate, they should
+	 * clear the high bit first.
+	 */
+	KASSERT(! (rate & 0x80), ("rate %d is basic/mcs?", rate));
+
 	return rt->rateCodeToIndex[rate] != (uint8_t)-1;
 }
 
@@ -134,6 +164,14 @@ ieee80211_ack_duration(const struct ieee
 	}
 }
 
+static __inline__ uint8_t
+ieee80211_legacy_rate_lookup(const struct ieee80211_rate_table *rt,
+    uint8_t rate)
+{
+
+	return (rt->rateCodeToIndex[rate & IEEE80211_RATE_VAL]);
+}
+
 /*
  * Compute the time to transmit a frame of length frameLen bytes
  * using the specified 802.11 rate code, phy, and short preamble
@@ -151,5 +189,10 @@ uint8_t		ieee80211_plcp2rate(uint8_t, en
  * Convert 802.11 rate code to PLCP signal.
  */
 uint8_t		ieee80211_rate2plcp(int, enum ieee80211_phytype);
+
+uint32_t	ieee80211_compute_duration_ht(uint32_t frameLen,
+			uint16_t rate, int streams, int isht40,
+			int isShortGI);
+
 #endif	/* _KERNEL */
 #endif	/* !_NET80211_IEEE80211_PHY_H_ */


More information about the svn-src-head mailing list