Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 25 Aug 2013 22:30:01 +0800
From:      Chenchong Qin <qinchenchong@gmail.com>
To:        Adrian Chadd <adrian@freebsd.org>
Cc:        "freebsd-wireless@freebsd.org" <freebsd-wireless@freebsd.org>
Subject:   Re: Chenchong's work on net80211_ratectl
Message-ID:  <CAFnsE3eMrwpo=hcFb9XfpLL53Ppso%2B%2BXTBfpieP8FGejAKW1_w@mail.gmail.com>
In-Reply-To: <CAFnsE3cS_2Ad1geQQ0UB4doEgqVfhNBMi6j4fCRpFgQqcu2kJw@mail.gmail.com>
References:  <CAFnsE3dYdPf5yGTFH683Q1Zh0mc-g%2B_YtCTraNNt28z2vBoSKw@mail.gmail.com> <CAJ-Vmom4sY7jcNwWmJkrDwfWjsok2fk8UEwTi5A=egj1JyerLw@mail.gmail.com> <CAFnsE3cyg=msBfQqqKUMmLABSL=j24VoMBwbBjxQ6b7Dyy7Mqg@mail.gmail.com> <CAJ-Vmo=k8NddAYyAJCkx4eOaA_8XsSxg6uKrdddx%2BgmeT%2BX9KA@mail.gmail.com> <CAFnsE3eaOyRcO3LXSi3L=jbzpyMv5Nt_jRGKt_mmA0WV-EV5vA@mail.gmail.com> <CAJ-VmokdxLhK5x6kO=jJzk-dv61EDK-ZgmndOimoyWWf76HiZA@mail.gmail.com> <CAJ-VmonMjR5iVTMVN9532d%2BPqOXWNUoZvxPtQir5h=yGxU-XdQ@mail.gmail.com> <CAFnsE3d9nG-X2b=z1srKfTtpxC3w5L%2B6Hg3TbOnAQrJN%2Bt19GA@mail.gmail.com> <CAJ-VmokF6hPtg9FoEdeJXLLaZRNhzd=nr_o6nHE%2BjYiQKTg3zQ@mail.gmail.com> <CAFnsE3eMwX-GiRzJt8jk4r9mxwSAQkcrDwk%2BnWVG7q6dabeA3A@mail.gmail.com> <CAJ-Vmo=mzvS0UBC7fGx2t501%2Bfioi4DJcw8qobOpbYOUiraqGg@mail.gmail.com> <CAFnsE3df=1WEuLZh5355v_K2eBgcuBbpoza74Y-5vvNupBz22A@mail.gmail.com> <CAJ-VmokyXwkKLdsJw74bux7G5EJSRvFhugTcLR9BgXfw4ysYRg@mail.gmail.com> <CAJ-VmokU=ZXysjZfAJ-REZL7kwg-_Z-LeAKca7AefONW_O1E5A@mail.gmail.com> <CAFnsE3cS_2Ad1geQQ0UB4doEgqVfhNBMi6j4fCRpFgQqcu2kJw@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Hi!

This is the latest update.

* add a simple per vap ratectl statistic tracker and api to update it.
* port irn_capabilities to irs_capabilities in struct ieee80211_rc_stat.
  perhaps the capabilities field needs to be within ieee80211_rc_stat as
  a per vap atrribute. corresponding updates performed.
* add ieee80211_ratectl_none.h to record common ratectl state.

Thanks!

Chenchong


On Thu, Aug 15, 2013 at 8:03 PM, Chenchong Qin <qinchenchong@gmail.com>wrote:

> Hi!
>
> Here is my latest update. In this update:
>
> * add a new struct, ieee80211_ratectl_node. This is the common state that
> all per node rc
>   state, i.e. ieee80211_[amrr|sample]_node, should contains it as the
> first field. It's now used to store
>   the capabilities. see below.
> * rename ir_capabilities to irn_capabilities and move it to
> ieee80211_ratectl_node (it contained in
>   ieee80211_[amrr|sample]_node). ieee80211_ratectl is readonly, so
> ir_capabilities can't be set. And,
>   the capabilities is not a part of rc algo. It seems that it should be
> put in the per node rc state. Interface
>   of ieee80211_ratectl_node_init() and its callers  are updated.
> References to ir_capabilities are also adapted.
> * add ieee80211_ratectl_[node_is11n|get_rateset] to the ratectl api. rc
> algoes all need these functions.
> * change the naming conversion of IEEE80211_RATECTL_FLAG_*.
> * some errors fixed.
>
>
> On Thu, Aug 15, 2013 at 12:58 AM, Adrian Chadd <adrian@freebsd.org> wrote:
>
>> Hi!
>>
>> So yes, we do need to have a generic way of returning that completion
>> information to the rate control code.
>>
>> I'm all for you churning the rate control API to return a struct
>> ieee80211_rc_info in the complete function and totally just kill arg1/arg2.
>> That forces us to extend ieee80211_rc_info to be "right" for all the
>> drivers.
>>
>
> Do you mean drop arg1/arg2 and pass pointer of ieee80211_rc_info to the
> complete function directly? Or return it
> when complete function return?
>
>
>> What wifi devices do you have there? It looks like we're almost at the
>> point where we can start converting a few things to use the modified rate
>> control API and modules - notably iwn (which won't use the multi-rate retry
>> stuff to begin with - it works "differently"..) and ath (which will use the
>> multi-rate retry stuff and the sample rate control module.)
>>
>
> Yeah, I have an AR9227 device at hand.
>
> And, I also get a question here. The ieee80211_ratetable doesn't get a
> rateCode field. So, how we get the
> ratecode of the non_ht rate?
>
> Thanks!
>
> Chenchong
>
>

[-- Attachment #2 --]
Index: sys/dev/iwn/if_iwn.c
===================================================================
--- sys/dev/iwn/if_iwn.c	(revision 254826)
+++ sys/dev/iwn/if_iwn.c	(working copy)
@@ -876,7 +876,8 @@
 	ivp->iv_newstate = vap->iv_newstate;
 	vap->iv_newstate = iwn_newstate;
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	/* Complete setup. */
 	ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
 	ic->ic_opmode = opmode;
Index: sys/dev/bwi/if_bwi.c
===================================================================
--- sys/dev/bwi/if_bwi.c	(revision 254826)
+++ sys/dev/bwi/if_bwi.c	(working copy)
@@ -617,7 +617,8 @@
 #if 0
 	vap->iv_update_beacon = bwi_beacon_update;
 #endif
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 
 	/* complete setup */
 	ieee80211_vap_attach(vap, bwi_media_change, ieee80211_media_status);
Index: sys/dev/bwn/if_bwn.c
===================================================================
--- sys/dev/bwn/if_bwn.c	(revision 254826)
+++ sys/dev/bwn/if_bwn.c	(working copy)
@@ -2970,7 +2970,8 @@
 	/* override max aid so sta's cannot assoc when we're out of sta id's */
 	vap->iv_max_aid = BWN_STAID_MAX;
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change,
Index: sys/dev/usb/wlan/if_ural.c
===================================================================
--- sys/dev/usb/wlan/if_ural.c	(revision 254826)
+++ sys/dev/usb/wlan/if_ural.c	(working copy)
@@ -598,7 +598,8 @@
 
 	usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0);
 	TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp);
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
 
 	/* complete setup */
Index: sys/dev/usb/wlan/if_rum.c
===================================================================
--- sys/dev/usb/wlan/if_rum.c	(revision 254826)
+++ sys/dev/usb/wlan/if_rum.c	(working copy)
@@ -613,7 +613,8 @@
 
 	usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0);
 	TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp);
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
Index: sys/dev/usb/wlan/if_run.c
===================================================================
--- sys/dev/usb/wlan/if_run.c	(revision 254826)
+++ sys/dev/usb/wlan/if_run.c	(working copy)
@@ -820,7 +820,8 @@
 	rvp->newstate = vap->iv_newstate;
 	vap->iv_newstate = run_newstate;
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
 
 	/* complete setup */
Index: sys/dev/usb/wlan/if_zyd.c
===================================================================
--- sys/dev/usb/wlan/if_zyd.c	(revision 254826)
+++ sys/dev/usb/wlan/if_zyd.c	(working copy)
@@ -496,7 +496,8 @@
 	zvp->newstate = vap->iv_newstate;
 	vap->iv_newstate = zyd_newstate;
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
 
 	/* complete setup */
Index: sys/dev/wpi/if_wpi.c
===================================================================
--- sys/dev/wpi/if_wpi.c	(revision 254826)
+++ sys/dev/wpi/if_wpi.c	(working copy)
@@ -782,7 +782,8 @@
 	wvp->newstate = vap->iv_newstate;
 	vap->iv_newstate = wpi_newstate;
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
 	ic->ic_opmode = opmode;
Index: sys/dev/ral/rt2560.c
===================================================================
--- sys/dev/ral/rt2560.c	(revision 254826)
+++ sys/dev/ral/rt2560.c	(working copy)
@@ -426,7 +426,8 @@
 	vap->iv_newstate = rt2560_newstate;
 	vap->iv_update_beacon = rt2560_beacon_update;
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
 	if (TAILQ_FIRST(&ic->ic_vaps) == vap)
Index: sys/dev/ral/rt2661.c
===================================================================
--- sys/dev/ral/rt2661.c	(revision 254826)
+++ sys/dev/ral/rt2661.c	(working copy)
@@ -423,7 +423,8 @@
 	vap->iv_update_beacon = rt2661_beacon_update;
 #endif
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
 	if (TAILQ_FIRST(&ic->ic_vaps) == vap)
Index: sys/dev/ral/rt2860.c
===================================================================
--- sys/dev/ral/rt2860.c	(revision 254826)
+++ sys/dev/ral/rt2860.c	(working copy)
@@ -484,7 +484,8 @@
 	/* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */
 	vap->iv_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX);
 
-	ieee80211_ratectl_init(vap);
+	/* XXX TODO fill the cap */
+	ieee80211_ratectl_init(vap, 0);
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
 	if (TAILQ_FIRST(&ic->ic_vaps) == vap)
Index: sys/net80211/ieee80211_ratectl_none.c
===================================================================
--- sys/net80211/ieee80211_ratectl_none.c	(revision 254826)
+++ sys/net80211/ieee80211_ratectl_none.c	(working copy)
@@ -44,10 +44,26 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_ratectl_none.h>
 
 static void
-none_init(struct ieee80211vap *vap)
+none_init(struct ieee80211vap *vap, uint32_t capabilities)
 {
+	struct ieee80211_node *none;
+
+	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
+
+	none = vap->iv_rs = malloc(sizeof(struct ieee80211_none),
+	    M_80211_RATECTL, M_NOWAIT|M_ZERO);
+	if (none == NULL) {
+		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
+		return;
+	}
+
+	struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+	irs->irs_capabilities = capabilities;
+
+	/* ... */
 }
 
 static void
@@ -59,6 +75,25 @@
 static void
 none_node_init(struct ieee80211_node *ni)
 {
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_none *none = vap->iv_rs;
+	struct ieee80211_none_node *non;
+
+	if (ni->ni_rctls == NULL) {
+		ni->ni_rctls = non = malloc(sizeof(struct ieee80211_none_node),
+		    M_80211_RATECTL, M_NOWAIT|M_ZERO);
+		if (non == NULL) {
+			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
+			    "structure\n");
+			return;
+		}
+	} else
+		non = ni->ni_rctls;
+
+	non->non_none = none;
+
+	/* ... */
+	
 	ni->ni_txrate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL;
 }
 
Index: sys/net80211/ieee80211_ratectl.c
===================================================================
--- sys/net80211/ieee80211_ratectl.c	(revision 254826)
+++ sys/net80211/ieee80211_ratectl.c	(working copy)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -49,6 +50,40 @@
 
 MALLOC_DEFINE(M_80211_RATECTL, "80211ratectl", "802.11 rate control");
 
+enum {
+	MCS_HT20,
+	MCS_HT20_SGI,
+	MCS_HT40,
+	MCS_HT40_SGI,
+};
+
+int max_4ms_framelen[4][32] = {
+	[MCS_HT20] = {
+		3212,  6432,  9648,  12864,  19300,  25736,  28952,  32172,
+		6424,  12852, 19280, 25708,  38568,  51424,  57852,  64280,
+		9628,  19260, 28896, 38528,  57792,  65532,  65532,  65532,
+		12828, 25656, 38488, 51320,  65532,  65532,  65532,  65532,
+	},
+	[MCS_HT20_SGI] = {
+		3572,  7144,  10720,  14296,  21444,  28596,  32172,  35744,
+		7140,  14284, 21428,  28568,  42856,  57144,  64288,  65532,
+		10700, 21408, 32112,  42816,  64228,  65532,  65532,  65532,
+		14256, 28516, 42780,  57040,  65532,  65532,  65532,  65532,
+	},
+	[MCS_HT40] = {
+		6680,  13360,  20044,  26724,  40092,  53456,  60140,  65532,
+		13348, 26700,  40052,  53400,  65532,  65532,  65532,  65532,
+		20004, 40008,  60016,  65532,  65532,  65532,  65532,  65532,
+		26644, 53292,  65532,  65532,  65532,  65532,  65532,  65532,
+	},
+	[MCS_HT40_SGI] = {
+		7420,  14844,  22272,  29696,  44544,  59396,  65532,  65532,
+		14832, 29668,  44504,  59340,  65532,  65532,  65532,  65532,
+		22232, 44464,  65532,  65532,  65532,  65532,  65532,  65532,
+		29616, 59232,  65532,  65532,  65532,  65532,  65532,  65532,
+	}
+};
+
 void
 ieee80211_ratectl_register(int type, const struct ieee80211_ratectl *ratectl)
 {
@@ -66,11 +101,11 @@
 }
 
 void
-ieee80211_ratectl_init(struct ieee80211vap *vap)
+ieee80211_ratectl_init(struct ieee80211vap *vap, uint32_t capabilities)
 {
 	if (vap->iv_rate == ratectls[IEEE80211_RATECTL_NONE])
 		ieee80211_ratectl_set(vap, IEEE80211_RATECTL_AMRR);
-	vap->iv_rate->ir_init(vap);
+	vap->iv_rate->ir_init(vap, capabilities);
 }
 
 void
@@ -90,3 +125,103 @@
 	}
 	vap->iv_rate = ratectls[type];
 }
+
+void
+ieee80211_ratectl_complete_rcflags(struct ieee80211_node *ni,
+		struct ieee80211_rc_info *rc_info)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	const struct ieee80211_rate_table * rt = NULL;
+	struct ieee80211_rc_series *rc = rc_info->iri_rc;
+	/* int shortPreamble = rc_info->ri_shortPreamble; */
+	uint8_t rate;
+	int i;
+
+	rt = ieee80211_get_ratetable(ni->ni_ic->ic_curchan);
+
+	/* Make sure that rate control code doesn't mess it up.
+	 * If enable rts/cts and is pre-802.11n, blank tries 1, 2, 3 
+	 */
+
+	if (! IEEE80211_RATECTL_HASCAP_MRRPROT(vap))
+	{
+		for (i = 1; i < IEEE80211_RATECTL_NUM; i++)
+		{
+			if (rc[0].flags & IEEE80211_RATECTL_FLAG_RTSCTS)
+				rc[i].tries = 0;
+			rc[i].flags &= ~IEEE80211_RATECTL_FLAG_RTSCTS; 
+		}
+	}
+
+	for (i = 0; i < IEEE80211_RATECTL_NUM; i++) {
+		
+		if (rc[i].tries == 0)
+			continue;
+
+		rate = rt->info[rc[i].rix].dot11Rate;
+
+		/*
+		 * Only enable short preamble for legacy rates
+		 */
+
+		/* XXX how we get the non_ht ratecode here? */
+
+		#if 0
+		if ((! IS_HT_RATE(rate)) && shortPreamble)
+			rate |= rt->info[rc[i].rix].shortPreamble;
+		#endif
+
+		/*
+		 * Save this, used by the TX and completion code
+		 */
+		rc[i].ratecode = rate;
+
+		/* Only enable shortgi, 2040, dual-stream if HT is set */
+		if (IS_HT_RATE(rate)) {
+			rc[i].flags |= IEEE80211_RATECTL_FLAG_HT;
+
+			/*
+			 * XXX TODO: LDPC
+			 */
+
+			/*
+			 * Dual / Triple stream rate?
+			 */
+			if (HT_RC_2_STREAMS(rate) == 2)
+				rc[i].flags |= IEEE80211_RATECTL_FLAG_DS;
+			else if (HT_RC_2_STREAMS(rate) == 3)
+				rc[i].flags |= IEEE80211_RATECTL_FLAG_TS;
+		}
+
+		/*
+		 * Calculate the maximum TX power cap for the current
+		 * node. 
+		 * Rate control algo can't control TX power by now.
+		 */
+		rc[i].tx_power_cap = ieee80211_get_node_txpower(ni);
+		
+		/*
+		 * Calculate the maximum 4ms frame length based
+		 * on the MCS rate, SGI and channel width flags.
+		 */
+		if ((rc[i].flags & IEEE80211_RATECTL_FLAG_HT) &&
+		    (HT_RC_2_MCS(rate) < 32)) {
+			int j;
+			if (rc[i].flags & IEEE80211_RATECTL_FLAG_CW40) {
+				if (rc[i].flags & IEEE80211_RATECTL_FLAG_SGI)
+					j = MCS_HT40_SGI;
+				else
+					j = MCS_HT40;
+			} else {
+				if (rc[i].flags & IEEE80211_RATECTL_FLAG_SGI)
+					j = MCS_HT20_SGI;
+				else
+					j = MCS_HT20;
+			}
+			rc[i].max4msframelen =
+			    max_4ms_framelen[j][HT_RC_2_MCS(rate)];
+		} else
+			rc[i].max4msframelen = 0;
+	}
+}
+
Index: sys/net80211/ieee80211_amrr.c
===================================================================
--- sys/net80211/ieee80211_amrr.c	(revision 254826)
+++ sys/net80211/ieee80211_amrr.c	(working copy)
@@ -47,8 +47,8 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_ht.h>
+#include <net80211/ieee80211_ratectl.h>
 #include <net80211/ieee80211_amrr.h>
-#include <net80211/ieee80211_ratectl.h>
 
 #define is_success(amn)	\
 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
@@ -58,7 +58,7 @@
 	((amn)->amn_txcnt > 10)
 
 static void	amrr_setinterval(const struct ieee80211vap *, int);
-static void	amrr_init(struct ieee80211vap *);
+static void	amrr_init(struct ieee80211vap *, uint32_t);
 static void	amrr_deinit(struct ieee80211vap *);
 static void	amrr_node_init(struct ieee80211_node *);
 static void	amrr_node_deinit(struct ieee80211_node *);
@@ -105,7 +105,7 @@
 }
 
 static void
-amrr_init(struct ieee80211vap *vap)
+amrr_init(struct ieee80211vap *vap, uint32_t capabilities)
 {
 	struct ieee80211_amrr *amrr;
 
@@ -117,6 +117,10 @@
 		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
 		return;
 	}
+
+	struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+	irs->irs_capabilities = capabilities;
+
 	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
 	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
 	amrr_setinterval(vap, 500 /* ms */);
@@ -129,17 +133,6 @@
 	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)
 {
@@ -159,6 +152,7 @@
 		}
 	} else
 		amn = ni->ni_rctls;
+
 	amn->amn_amrr = amrr;
 	amn->amn_success = 0;
 	amn->amn_recovery = 0;
@@ -165,23 +159,13 @@
 	amn->amn_txcnt = amn->amn_retrycnt = 0;
 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
 
-	/* 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;
-	}
+	rs = ieee80211_ratectl_get_rateset(ni);
 
 	/* 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))
+	if (! ieee80211_ratectl_node_is11n(ni))
 		rate &= IEEE80211_RATE_VAL;
 
 	/* pick initial rate from the rateset - HT or otherwise */
@@ -189,7 +173,7 @@
 	    amn->amn_rix--) {
 		/* legacy - anything < 36mbit, stop searching */
 		/* 11n - stop at MCS4 / MCS12 / MCS28 */
-		if (amrr_node_is_11n(ni) &&
+		if (ieee80211_ratectl_node_is11n(ni) &&
 		    (rs->rs_rates[amn->amn_rix] & 0x7) < 4)
 			break;
 		else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
@@ -198,7 +182,7 @@
 	}
 
 	/* if the rate is an 11n rate, ensure the MCS bit is set */
-	if (amrr_node_is_11n(ni))
+	if (ieee80211_ratectl_node_is11n(ni))
 		rate |= IEEE80211_RATE_MCS;
 
 	/* Assign initial rate from the rateset */
@@ -226,14 +210,9 @@
 
 	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;
-	}
+	rs = ieee80211_ratectl_get_rateset(ni);
 
+
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
 	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
 	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
@@ -304,13 +283,7 @@
 	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;
-	}
+	rs = ieee80211_ratectl_get_rateset(ni);
 
 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
 		rix = amrr_update(amrr, amn, ni);
@@ -318,7 +291,7 @@
 			/* update public rate */
 			ni->ni_txrate = rs->rs_rates[rix];
 			/* XXX strip basic rate flag from txrate, if non-11n */
-			if (amrr_node_is_11n(ni))
+			if (ieee80211_ratectl_node_is11n(ni))
 				ni->ni_txrate |= IEEE80211_RATE_MCS;
 			else
 				ni->ni_txrate &= IEEE80211_RATE_VAL;
@@ -338,11 +311,17 @@
 static void
 amrr_tx_complete(const struct ieee80211vap *vap,
     const struct ieee80211_node *ni, int ok,
-    void *arg1, void *arg2 __unused)
+    void *arg1, void *arg2)
 {
 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
 	int retries = *(int *)arg1;
 
+	/* XXX need to change arg2 to pointer of ieee80211_rc_info */
+	struct ieee80211_rc_info *rc_info = (struct ieee80211_rc_info*)arg2;
+
+	/* update per vap statistics */
+	ieee80211_ratectl_update_stat(vap, rc_info);
+
 	amn->amn_txcnt++;
 	if (ok)
 		amn->amn_success++;
Index: sys/net80211/ieee80211_ratectl.h
===================================================================
--- sys/net80211/ieee80211_ratectl.h	(revision 254826)
+++ sys/net80211/ieee80211_ratectl.h	(working copy)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,6 +25,8 @@
  *
  * $FreeBSD$
  */
+#ifndef _NET80211_IEEE80211_RATECTL_H_
+#define _NET80211_IEEE80211_RATECTL_H_
 
 enum ieee80211_ratealgs {
 	IEEE80211_RATECTL_AMRR		= 0,
@@ -37,15 +40,91 @@
 #define	IEEE80211_RATECTL_TX_SUCCESS	1
 #define	IEEE80211_RATECTL_TX_FAILURE	0
 
+#define	IEEE80211_RATECTL_TRUE		1
+#define	IEEE80211_RATECTL_FALSE		0
+
+#define	IEEE80211_RATECTL_NUM		4
+
+#define	IEEE80211_RATECTL_FLAG_DS		0x01	/* dual-stream rate */
+#define	IEEE80211_RATECTL_FLAG_CW40		0x02	/* use HT40 */
+#define	IEEE80211_RATECTL_FLAG_SGI		0x04	/* use short-GI */
+#define	IEEE80211_RATECTL_FLAG_HT		0x08	/* use HT */
+#define	IEEE80211_RATECTL_FLAG_RTSCTS	0x10	/* enable RTS/CTS protection */
+#define	IEEE80211_RATECTL_FLAG_STBC		0x20	/* enable STBC */
+#define	IEEE80211_RATECTL_FLAG_TS		0x40	/* triple-stream rate */
+
+/* Hardware CAPs offered to rate control algo */
+#define	IEEE80211_RATECTL_CAP_MRR			0x01	/* support MRR */
+#define	IEEE80211_RATECTL_CAP_MRRPROT		0x02	/* support MRR + protect */
+#define	IEEE80211_RATECTL_CAP_MULTXCHAIN	0x04	/* has more than 1 txchain */
+
+#define IS_VAP_HT(vap)	((vap)->iv_htcaps & IEEE80211_HTC_HT)
+#define IS_HT_RATE(_rate)   ((_rate) & 0x80)
+#define HT_RC_2_MCS(_rc)    ((_rc) & 0x7f)
+#define HT_RC_2_STREAMS(_rc)    ((((_rc) & 0x78) >> 3) + 1)
+
+extern int max_4ms_framelen[4][32];
+
+struct ieee80211_rc_series {
+	uint8_t rix;		/* ratetable index, not rate code */
+	uint8_t ratecode;	/* hardware rate code */
+	uint8_t tries;
+	uint8_t tx_power_cap;
+	uint16_t flags;
+	uint16_t max4msframelen;
+};
+
+struct ieee80211_rc_info {
+	struct ieee80211_rc_series iri_rc[IEEE80211_RATECTL_NUM];
+	int iri_framelen;
+	int iri_shortPreamble;
+
+	/* TX info */
+	int iri_success;	/* TX success or not */
+	int iri_okcnt;		/* TX ok with or without retry */
+	int iri_failcnt;	/* TX retry-fail count */
+	int iri_txcnt;		/* TX count */
+	int iri_retrycnt;	/* TX retry count */
+	int iri_shortretry;
+	int iri_longretry;
+	int iri_finaltsi;
+	int iri_txrate;		/* hw tx rate */
+};
+
+/* net80211 ratectl statistics. 
+ * per vap ratectl seeting must start with this common state
+ */
+struct ieee80211_rc_stat {
+	uint32_t irs_capabilities;		/* hardware capabilities offered to rc */
+
+	/* ratectl statistics */
+	uint32_t irs_txcnt;
+	uint32_t irs_failcnt;
+	uint32_t irs_retrycnt;
+	uint32_t irs_shortretry;
+	uint32_t irs_longretry;
+};
+
+#define IEEE80211_RATECTL_STAT(_vap) \
+	((struct ieee80211_rc_stat *)((_vap)->iv_rs))
+
+#define	IEEE80211_RATECTL_HASCAP_MRR(_vap) \
+	(IEEE80211_RATECTL_STAT(_vap)->irs_capabilities & IEEE80211_RATECTL_CAP_MRR)
+#define	IEEE80211_RATECTL_HASCAP_MRRPROT(_vap) \
+	(IEEE80211_RATECTL_STAT(_vap)->irs_capabilities & IEEE80211_RATECTL_CAP_MRRPROT)
+#define	IEEE80211_RATECTL_HASCAP_MULTXCHAIN(_vap) \
+	(IEEE80211_RATECTL_STAT(_vap)->irs_capabilities & IEEE80211_RATECTL_CAP_MULTXCHAIN)
+
 struct ieee80211_ratectl {
 	const char *ir_name;
 	int	(*ir_attach)(const struct ieee80211vap *);
 	void	(*ir_detach)(const struct ieee80211vap *);
-	void	(*ir_init)(struct ieee80211vap *);
+	void	(*ir_init)(struct ieee80211vap *, uint32_t);
 	void	(*ir_deinit)(struct ieee80211vap *);
 	void	(*ir_node_init)(struct ieee80211_node *);
 	void	(*ir_node_deinit)(struct ieee80211_node *);
 	int	(*ir_rate)(struct ieee80211_node *, void *, uint32_t);
+	void	(*ir_rates)(struct ieee80211_node *, struct ieee80211_rc_info *);
 	void	(*ir_tx_complete)(const struct ieee80211vap *,
 	    			  const struct ieee80211_node *, int,
 	    			  void *, void *);
@@ -57,8 +136,10 @@
 
 void	ieee80211_ratectl_register(int, const struct ieee80211_ratectl *);
 void	ieee80211_ratectl_unregister(int);
-void	ieee80211_ratectl_init(struct ieee80211vap *);
+void	ieee80211_ratectl_init(struct ieee80211vap *, uint32_t);
 void	ieee80211_ratectl_set(struct ieee80211vap *, int);
+void	ieee80211_ratectl_complete_rcflags(struct ieee80211_node *, 
+						struct ieee80211_rc_info*);
 
 MALLOC_DECLARE(M_80211_RATECTL);
 
@@ -93,6 +174,15 @@
 }
 
 static void __inline
+ieee80211_ratectl_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
+{
+	const struct ieee80211vap *vap = ni->ni_vap;
+
+	vap->iv_rate->ir_rates(ni, rc_info);
+	ieee80211_ratectl_complete_rcflags(ni, rc_info);
+}
+
+static void __inline
 ieee80211_ratectl_tx_complete(const struct ieee80211vap *vap,
     const struct ieee80211_node *ni, int status, void *arg1, void *arg2)
 {
@@ -115,3 +205,71 @@
 		return;
 	vap->iv_rate->ir_setinterval(vap, msecs);
 }
+
+static int __inline
+ieee80211_ratectl_hascap_cw40(const struct ieee80211vap *vap,
+		const struct ieee80211_node *ni)
+{
+	return IS_VAP_HT(vap) && (ni->ni_chw == 40);
+}
+
+static int __inline
+ieee80211_ratectl_hascap_shortgi(const struct ieee80211vap *vap,
+		const struct ieee80211_node *ni)
+{
+	if (IS_VAP_HT(vap))
+	{
+		if (ni->ni_chw == 40 &&
+				vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40 &&
+				ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+			return IEEE80211_RATECTL_TRUE;
+
+		if (ni->ni_chw == 20 &&
+				vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20 &&
+				ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20)
+			return IEEE80211_RATECTL_TRUE;
+	}
+	return IEEE80211_RATECTL_FALSE;
+}
+
+
+static int __inline
+ieee80211_ratectl_hascap_stbc(const struct ieee80211vap *vap,
+		const struct ieee80211_node *ni)
+{
+   return IS_VAP_HT(vap) && (vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC) &&
+			    (ni->ni_htcap & IEEE80211_HTCAP_RXSTBC_1STREAM) &&
+			    IEEE80211_RATECTL_HASCAP_MULTXCHAIN(vap);
+}
+
+static int __inline
+ieee80211_ratectl_node_is11n(const 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));
+}
+
+__inline static const struct ieee80211_rateset *
+ieee80211_ratectl_get_rateset(const struct ieee80211_node *ni)
+{
+	return ieee80211_ratectl_node_is11n(ni) ? 
+				(struct ieee80211_rateset *) &ni->ni_htrates :
+				&ni->ni_rates;
+}
+
+static void __inline
+ieee80211_ratectl_update_stat(const struct ieee80211vap *vap,
+		const struct ieee80211_rc_info *rc_info)
+{
+	struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+	irs->irs_txcnt += rc_info->iri_txcnt;
+	irs->irs_failcnt += rc_info->iri_failcnt;
+	irs->irs_retrycnt += rc_info->iri_retrycnt;
+	irs->irs_shortretry += rc_info->iri_shortretry;
+	irs->irs_longretry += rc_info->iri_longretry;
+}
+
+#endif
Index: sys/net80211/ieee80211_amrr.h
===================================================================
--- sys/net80211/ieee80211_amrr.h	(revision 254826)
+++ sys/net80211/ieee80211_amrr.h	(working copy)
@@ -20,6 +20,7 @@
 #ifndef _NET80211_IEEE80211_AMRR_H_
 #define _NET80211_IEEE80211_AMRR_H_
 
+#include <net80211/ieee80211_ratectl.h>
 /*-
  * Naive implementation of the Adaptive Multi Rate Retry algorithm:
  *
@@ -35,6 +36,7 @@
 struct ieee80211vap;
 
 struct ieee80211_amrr {
+	struct ieee80211_rc_stat amrr_stat;
 	u_int	amrr_min_success_threshold;
 	u_int	amrr_max_success_threshold;
 	int	amrr_interval;		/* update interval (ticks) */
Index: sys/net80211/ieee80211_ratectl_none.h
===================================================================
--- sys/net80211/ieee80211_ratectl_none.h	(revision 0)
+++ sys/net80211/ieee80211_ratectl_none.h	(revision 256489)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _NET80211_IEEE80211_RATECTL_NONE_H_
+#define _NET80211_IEEE80211_RATECTL_NONE_H_
+
+#include <net80211/ieee80211_ratectl.h>
+
+/*
+ * Rate control settings.
+ */
+
+struct ieee80211_none {
+	struct ieee80211_rc_stat none_stat;
+	/* other stuffs */
+};
+
+/*
+ * Rate control state for a given node.
+ */
+struct ieee80211_none_node {
+	struct ieee80211_none *non_none;
+};
+
+#endif /* _NET80211_IEEE80211_RATECTL_NONE_H_ */
Index: sys/net80211/ieee80211_rc_sample.c
===================================================================
--- sys/net80211/ieee80211_rc_sample.c	(revision 0)
+++ sys/net80211/ieee80211_rc_sample.c	(revision 256474)
@@ -0,0 +1,1141 @@
+/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.c 248573 2013-02-27 04:33:06Z adrian $*/
+
+/*-
+ * Copyright (c) 2005 John Bicket
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ht.h>
+#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_rc_sample.h>
+#include <net80211/ieee80211_rc_sample_txsched.h>
+
+static void	sample_init(struct ieee80211vap *, uint32_t);
+static void	sample_deinit(struct ieee80211vap *);
+static void	sample_node_init(struct ieee80211_node *);
+static void	sample_node_deinit(struct ieee80211_node *);
+static int	sample_rate(struct ieee80211_node *, void *, uint32_t);
+static void	sample_tx_complete(const struct ieee80211vap *,
+    			const struct ieee80211_node *, int, 
+			void *, void *);
+static void	sample_tx_update(const struct ieee80211vap *vap,
+			const struct ieee80211_node *, void *, void *, void *);
+static void	sample_setinterval(const struct ieee80211vap *, int);
+
+/* number of references from net80211 layer */
+static	int nrefs = 0;
+
+static const struct ieee80211_ratectl sample = {
+	.ir_name	= "sample",
+	.ir_attach	= NULL,
+	.ir_detach	= NULL,
+	.ir_init	= sample_init,
+	.ir_deinit	= sample_deinit,
+	.ir_node_init	= sample_node_init,
+	.ir_node_deinit	= sample_node_deinit,
+	.ir_rate	= sample_rate,
+	.ir_rates	= sample_rates,
+	.ir_tx_complete	= sample_tx_complete,
+	.ir_tx_update	= sample_tx_update,
+	.ir_setinterval	= sample_setinterval,
+};
+IEEE80211_RATECTL_MODULE(sample, 1);
+IEEE80211_RATECTL_ALG(sample, IEEE80211_RATECTL_SAMPLE, sample);
+
+static void
+sample_init(struct ieee80211vap *vap, uint32_t capabilities)
+{
+	struct ieee80211_sample *sample;
+
+	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
+
+	sample = vap->iv_rs = malloc(sizeof(struct ieee80211_sample),
+	    M_80211_RATECTL, M_NOWAIT|M_ZERO);
+	if (sample == NULL) {
+		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
+		return;
+	}
+
+	struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+	irs->irs_capabilities = capabilities;
+
+	sample->sample_smoothing_rate = 75;		/* ewma percentage ([0..99]) */
+	sample->sample_smoothing_minpackets = 100 / (100 - sample->sample_smoothing_rate);
+	sample->sample_rate = 10;			/* %time to try diff tx rates */
+	sample->sample_max_successive_failures = 3;	/* threshold for rate sampling*/
+	sample->sample_stale_failure_timeout = 10 * hz;	/* 10 seconds */
+	sample->sample_min_switch = hz;			/* 1 second */
+	sample_setinterval(vap, 500 /* ms */); 	/* actually do nothing */
+	sample_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
+}
+
+static void
+sample_deinit(struct ieee80211vap *vap)
+{
+	free(vap->iv_rs, M_80211_RATECTL);
+}
+
+static const struct txschedule *mrr_schedules[IEEE80211_MODE_MAX+2] = {
+	NULL,		/* IEEE80211_MODE_AUTO */
+	series_11a,	/* IEEE80211_MODE_11A */
+	series_11g,	/* IEEE80211_MODE_11B */
+	series_11g,	/* IEEE80211_MODE_11G */
+	NULL,		/* IEEE80211_MODE_FH */
+	series_11a,	/* IEEE80211_MODE_TURBO_A */
+	series_11g,	/* IEEE80211_MODE_TURBO_G */
+	series_11a,	/* IEEE80211_MODE_STURBO_A */
+	series_11na,	/* IEEE80211_MODE_11NA */
+	series_11ng,	/* IEEE80211_MODE_11NG */
+	series_half,	/* IEEE80211_MODE_HALF */
+	series_quarter,	/* IEEE80211_MODE_QUARTER */
+};
+
+static void
+sample_node_init(struct ieee80211_node *ni)
+{
+#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+#define	DOT11RATE(_ix)	(rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL)
+#define	MCS(_ix)	(ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS)
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_sample *sample = vap->iv_rs;
+	struct ieee80211_sample_node *san = NULL;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	enum ieee80211_phymode curmode = ieee80211_chan2mode(vap->iv_ic->ic_curchan);
+	int x, y, rix;
+
+	if (ni->ni_rctls == NULL) {
+		ni->ni_rctls = san = malloc(sizeof(struct ieee80211_sample_node),
+		    M_80211_RATECTL, M_NOWAIT|M_ZERO);
+		if (san == NULL) {
+			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
+			    "structure\n");
+			return;
+		}
+	} else
+		san = ni->ni_rctls;
+	
+	san->san_sample = sample;
+
+	KASSERT(rt != NULL, ("no rate table, mode %u", curmode));
+
+	san->sched = mrr_schedules[curmode];
+	KASSERT(san->sched != NULL, ("no mrr schedule for mode %u", curmode));
+
+	san->static_rix = -1;
+	sample_update_static_rix(ni);
+
+	/*
+	 * Construct a bitmask of usable rates.  This has all
+	 * negotiated rates minus those marked by the hal as
+	 * to be ignored for doing rate control.
+	 */
+	san->ratemask = 0;
+
+	/* MCS rates */
+	if (ni->ni_flags & IEEE80211_NODE_HT) {
+		for (x = 0; x < ni->ni_htrates.rs_nrates; x++) {
+			rix = rt->rateCodeToIndex[MCS(x)];
+			if (rix == 0xff)
+				continue;
+			/* skip rates marked broken by hal */
+			if (!rt->info[rix].valid)
+				continue;
+			KASSERT(rix < SAMPLE_MAXRATES,
+			    ("mcs %u has rix %d", MCS(x), rix));
+			san->ratemask |= (uint64_t) 1<<rix;
+		}
+	}
+
+	/* Legacy rates */
+	for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+		rix = rt->rateCodeToIndex[RATE(x)];
+		if (rix == 0xff)
+			continue;
+		/* skip rates marked broken by hal */
+		if (!rt->info[rix].valid)
+			continue;
+		KASSERT(rix < SAMPLE_MAXRATES,
+		    ("rate %u has rix %d", RATE(x), rix));
+		san->ratemask |= (uint64_t) 1<<rix;
+	}
+
+	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+		int size = bin_to_size(y);
+		uint64_t mask;
+
+		san->packets_sent[y] = 0;
+		san->current_sample_rix[y] = -1;
+		san->last_sample_rix[y] = 0;
+		/* XXX start with first valid rate */
+		san->current_rix[y] = ffs(san->ratemask)-1;
+		
+		/*
+		 * Initialize the statistics buckets; these are
+		 * indexed by the rate code index.
+		 */
+		for (rix = 0, mask = san->ratemask; mask != 0; rix++, mask >>= 1) {
+			if ((mask & 1) == 0)		/* not a valid rate */
+				continue;
+			san->stats[y][rix].successive_failures = 0;
+			san->stats[y][rix].tries = 0;
+			san->stats[y][rix].total_packets = 0;
+			san->stats[y][rix].packets_acked = 0;
+			san->stats[y][rix].last_tx = 0;
+			san->stats[y][rix].ewma_pct = 0;
+			
+			san->stats[y][rix].perfect_tx_time =
+			    calc_usecs_unicast_packet(vap, size, rix, 0, 0,
+			    (ni->ni_chw == 40));
+			san->stats[y][rix].average_tx_time =
+			    san->stats[y][rix].perfect_tx_time;
+		}
+	}
+
+	/* set the visible bit-rate */
+	if (san->static_rix != -1)
+		ni->ni_txrate = DOT11RATE(san->static_rix);
+	else
+		ni->ni_txrate = RATE(0);
+#undef RATE
+#undef DOT11RATE
+#undef MCS
+}
+
+static void
+sample_node_deinit(struct ieee80211_node *ni)
+{
+	free(ni->ni_rctls, M_80211_RATECTL);
+}
+
+static int
+dot11rate(const ieee80211_rate_table *rt, int rix)
+{
+	if (rix < 0)
+		return -1;
+	return rt->info[rix].phy == IEEE80211_T_HT ?
+	    rt->info[rix].dot11Rate : (rt->info[rix].dot11Rate & IEEE80211_RATE_VAL) / 2;
+}
+
+static const char *
+dot11rate_label(const ieee80211_rate_table *rt, int rix)
+{
+	if (rix < 0)
+		return "";
+	return rt->info[rix].phy == IEEE80211_T_HT ? "MCS" : "Mb ";
+}
+
+/*
+ * Return the rix with the lowest average_tx_time,
+ * or -1 if all the average_tx_times are 0.
+ */
+static __inline int
+pick_best_rate(const struct ieee80211_node *ni, const struct ieee80211_rate_table *rt,
+    int size_bin, int require_acked_before)
+{
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+    int best_rate_rix, best_rate_tt, best_rate_pct;
+	uint64_t mask;
+	int rix, tt, pct;
+
+    best_rate_rix = 0;
+    best_rate_tt = 0;
+	best_rate_pct = 0;
+
+	for (mask = san->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
+		if ((mask & 1) == 0)		/* not a supported rate */
+			continue;
+
+		/* Don't pick a non-HT rate for a HT node */
+		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+		    (rt->info[rix].phy != IEEE80211_T_HT)) {
+			continue;
+		}
+
+		tt = san->stats[size_bin][rix].average_tx_time;
+		if (tt <= 0 ||
+		    (require_acked_before &&
+		     !san->stats[size_bin][rix].packets_acked))
+			continue;
+
+		/* Calculate percentage if possible */
+		if (san->stats[size_bin][rix].total_packets > 0) {
+			pct = san->stats[size_bin][rix].ewma_pct;
+		} else {
+			/* XXX for now, assume 95% ok */
+			pct = 95;
+		}
+
+		/* don't use a bit-rate that has been failing */
+		if (san->stats[size_bin][rix].successive_failures > 3)
+			continue;
+
+		/*
+		 * For HT, Don't use a bit rate that is much more
+		 * lossy than the best.
+		 *
+		 * XXX this isn't optimal; it's just designed to
+		 * eliminate rates that are going to be obviously
+		 * worse.
+		 */
+		if (ni->ni_flags & IEEE80211_NODE_HT) {
+			if (best_rate_pct > (pct + 50))
+				continue;
+		}
+
+		/*
+		 * For non-MCS rates, use the current average txtime for
+		 * comparison.
+		 */
+		if (! (ni->ni_flags & IEEE80211_NODE_HT)) {
+			if (best_rate_tt == 0 || tt <= best_rate_tt) {
+				best_rate_tt = tt;
+				best_rate_rix = rix;
+				best_rate_pct = pct;
+			}
+		}
+
+		/*
+		 * Since 2 stream rates have slightly higher TX times,
+		 * allow a little bit of leeway. This should later
+		 * be abstracted out and properly handled.
+		 */
+		if (ni->ni_flags & IEEE80211_NODE_HT) {
+			if (best_rate_tt == 0 || (tt * 8 <= best_rate_tt * 10)) {
+				best_rate_tt = tt;
+				best_rate_rix = rix;
+				best_rate_pct = pct;
+			}
+		}
+        }
+        return (best_rate_tt ? best_rate_rix : -1);
+}
+
+/*
+ * Pick a good "random" bit-rate to sample other than the current one.
+ */
+static __inline int
+pick_sample_rate(const struct ieee80211_node *ni, const ieee80211_rate_table *rt, 
+	int size_bin)
+{
+#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	struct ieee80211_sample *sample = san->san_sample;
+	int current_rix, rix;
+	unsigned current_tt;
+	uint64_t mask;
+	
+	current_rix = san->current_rix[size_bin];
+	if (current_rix < 0) {
+		/* no successes yet, send at the lowest bit-rate */
+		/* XXX should return MCS0 if HT */
+		return 0;
+	}
+
+	current_tt = san->stats[size_bin][current_rix].average_tx_time;
+
+	rix = san->last_sample_rix[size_bin]+1;	/* next sample rate */
+	mask = san->ratemask &~ ((uint64_t) 1<<current_rix);/* don't sample current rate */
+	while (mask != 0) {
+		if ((mask & ((uint64_t) 1<<rix)) == 0) {	/* not a supported rate */
+	nextrate:
+			if (++rix >= rt->rateCount)
+				rix = 0;
+			continue;
+		}
+
+		/*
+		 * The following code stops trying to sample
+		 * non-MCS rates when speaking to an MCS node.
+		 * However, at least for CCK rates in 2.4GHz mode,
+		 * the non-MCS rates MAY actually provide better
+		 * PER at the very far edge of reception.
+		 *
+		 * However! Until ath_rate_form_aggr() grows
+		 * some logic to not form aggregates if the
+		 * selected rate is non-MCS, this won't work.
+		 *
+		 * So don't disable this code until you've taught
+		 * ath_rate_form_aggr() to drop out if any of
+		 * the selected rates are non-MCS.
+		 */
+#if 1
+		/* if the node is HT and the rate isn't HT, don't bother sample */
+		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+		    (rt->info[rix].phy != IEEE80211_T_HT)) {
+			mask &= ~((uint64_t) 1<<rix);
+			goto nextrate;
+		}
+#endif
+
+		/* this bit-rate is always worse than the current one */
+		if (san->stats[size_bin][rix].perfect_tx_time > current_tt) {
+			mask &= ~((uint64_t) 1<<rix);
+			goto nextrate;
+		}
+
+		/* rarely sample bit-rates that fail a lot */
+		if (san->stats[size_bin][rix].successive_failures > sample->sample_max_successive_failures &&
+		    ticks - san->stats[size_bin][rix].last_tx < sample->sample_stale_failure_timeout) {
+			mask &= ~((uint64_t) 1<<rix);
+			goto nextrate;
+		}
+
+		/*
+		 * For HT, only sample a few rates on either side of the
+		 * current rix; there's quite likely a lot of them.
+		 */
+		if (ni->ni_flags & IEEE80211_NODE_HT) {
+			if (rix < (current_rix - 3) ||
+			    rix > (current_rix + 3)) {
+				mask &= ~((uint64_t) 1<<rix);
+				goto nextrate;
+			}
+		}
+
+		/* Don't sample more than 2 rates higher for rates > 11M for non-HT rates */
+		if (! (ni->ni_flags & IEEE80211_NODE_HT)) {
+			if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) {
+				mask &= ~((uint64_t) 1<<rix);
+				goto nextrate;
+			}
+		}
+
+		san->last_sample_rix[size_bin] = rix;
+		return rix;
+	}
+	return current_rix;
+#undef DOT11RATE
+#undef MCS
+}
+
+static int
+sample_get_static_rix(const struct ieee80211_node *ni)
+{
+#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+#define	MCS(_ix)	(ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS)
+	struct ieee80211vap *vap = ni->ni_vap;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	const struct ieee80211_txparam *tp = ni->ni_txparms;
+	int srate;
+
+	/* Check MCS rates */
+	for (srate = ni->ni_htrates.rs_nrates - 1; srate >= 0; srate--) {
+		if (MCS(srate) == tp->ucastrate)
+			return rt->rateCodeToIndex[tp->ucastrate];
+	}
+
+	/* Check legacy rates */
+	for (srate = ni->ni_rates.rs_nrates - 1; srate >= 0; srate--) {
+		if (RATE(srate) == tp->ucastrate)
+			return rt->rateCodeToIndex[tp->ucastrate];
+	}
+	return -1;
+#undef	RATE
+#undef	MCS
+}
+
+static void
+sample_update_static_rix(struct ieee80211_node *ni)
+{
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	const struct ieee80211_txparam *tp = ni->ni_txparms;
+
+	if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+		/*
+		 * A fixed rate is to be used; ucastrate is the IEEE code
+		 * for this rate (sans basic bit).  Check this against the
+		 * negotiated rate set for the node.  Note the fixed rate
+		 * may not be available for various reasons so we only
+		 * setup the static rate index if the lookup is successful.
+		 */
+		san->static_rix = sample_get_static_rix(ni);
+	} else {
+		san->static_rix = -1;
+	}
+}
+
+/*
+ * Pick a non-HT rate to begin using.
+ */
+static int
+sample_pick_seed_rate_legacy(const struct ieee80211_node *ni, int frameLen)
+{
+#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	
+	const int size_bin = size_to_bin(frameLen);
+	int rix = -1;
+
+	/* no packet has been sent successfully yet */
+	for (rix = rt->rateCount-1; rix > 0; rix--) {
+		if ((san->ratemask & ((uint64_t) 1<<rix)) == 0)
+			continue;
+
+		/* Skip HT rates */
+		if (rt->info[rix].phy == IEEE80211_T_HT)
+			continue;
+
+		/*
+		 * Pick the highest rate <= 36 Mbps
+		 * that hasn't failed.
+		 */
+		if (DOT11RATE(rix) <= 72 &&
+		    san->stats[size_bin][rix].successive_failures == 0) {
+			break;
+		}
+	}
+	return rix;
+#undef DOT11RATE
+}
+
+/*
+ * Pick a HT rate to begin using.
+ *
+ * Don't use any non-HT rates; only consider HT rates.
+ */
+static int
+sample_pick_seed_rate_ht(const struct ieee80211_node *ni, int frameLen)
+{
+#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	
+	const int size_bin = size_to_bin(frameLen);
+	int rix = -1, ht_rix = -1;
+
+	/* no packet has been sent successfully yet */
+	for (rix = rt->rateCount-1; rix > 0; rix--) {
+		/* Skip rates we can't use */
+		if ((san->ratemask & ((uint64_t) 1<<rix)) == 0)
+			continue;
+
+		/* Keep a copy of the last seen HT rate index */
+		if (rt->info[rix].phy == IEEE80211_T_HT)
+			ht_rix = rix;
+
+		/* Skip non-HT rates */
+		if (rt->info[rix].phy != IEEE80211_T_HT)
+			continue;
+
+		/*
+		 * Pick a medium-speed rate regardless of stream count
+		 * which has not seen any failures. Higher rates may fail;
+		 * we'll try them later.
+		 */
+		if (((MCS(rix) & 0x7) <= 4) &&
+		    san->stats[size_bin][rix].successive_failures == 0) {
+			break;
+		}
+	}
+
+	/*
+	 * If all the MCS rates have successive failures, rix should be
+	 * > 0; otherwise use the lowest MCS rix (hopefully MCS 0.)
+	 */
+	return MAX(rix, ht_rix);
+#undef MCS
+}
+
+static int
+sample_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
+{
+#define	DOT11RATE(ix)	(rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+#define	MCS(ix)		(rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+#define	RATE(ix)	(DOT11RATE(ix) / 2)
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	struct ieee80211_sample *sample = san->san_sample;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	
+	int rix, mrr, best_rix, change_rates;
+	unsigned average_tx_time;
+	
+	size_t frameLen = (size_t)iarg;
+	const int size_bin = size_to_bin(frameLen);
+
+	sample_update_static_rix(ni);
+
+	if (san->static_rix != -1) {
+		rix = san->static_rix;
+		goto done;
+	}
+
+	if (IEEE80211_RATECTL_HASCAP_MRR(vap))
+		mrr = 1;
+	if (! IEEE80211_RATECTL_HASCAP_MRRPROT(vap))
+		mrr = 0;
+
+	best_rix = pick_best_rate(ni, rt, size_bin, !mrr);
+	if (best_rix >= 0) {
+		average_tx_time = san->stats[size_bin][best_rix].average_tx_time;
+	} else {
+		average_tx_time = 0;
+	}
+
+	/*
+	 * Limit the time measuring the performance of other tx
+	 * rates to sample_rate% of the total transmission time.
+	 */
+	if (san->sample_tt[size_bin] < average_tx_time * (san->packets_since_sample[size_bin] *
+		sample->sample_rate/100)) {
+		rix = pick_sample_rate(ni, rt, size_bin);
+		IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL,
+		     &ni, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s",
+		     average_tx_time,
+		     san->sample_tt[size_bin],
+		     bin_to_size(size_bin),
+		     dot11rate(rt, rix),
+		     dot11rate_label(rt, rix),
+		     dot11rate(rt, san->current_rix[size_bin]),
+		     dot11rate_label(rt, san->current_rix[size_bin]));
+		if (rix != san->current_rix[size_bin]) {
+			san->current_sample_rix[size_bin] = rix;
+		} else {
+			san->current_sample_rix[size_bin] = -1;
+		}
+		san->packets_since_sample[size_bin] = 0;
+	} else {
+		change_rates = 0;
+		if (!san->packets_sent[size_bin] || best_rix == -1) {
+			/* no packet has been sent successfully yet */
+			change_rates = 1;
+			if (ni->ni_flags & IEEE80211_NODE_HT)
+				best_rix = sample_pick_seed_rate_ht(ni, frameLen);
+			else
+				best_rix = sample_pick_seed_rate_legacy(ni, frameLen);
+		} else if (san->packets_sent[size_bin] < 20) {
+			/* let the bit-rate switch quickly during the first few packets */
+			IEEE80211_NOTE(vap,
+			    IEEE80211_MSG_RATECTL, &ni,
+			    "%s: switching quickly..", __func__);
+			change_rates = 1;
+		} else if (ticks - sample->sample_min_switch > san->ticks_since_switch[size_bin]) {
+			/* min_switch seconds have gone by */
+			IEEE80211_NOTE(vap,
+			    IEEE80211_MSG_RATECTL, &ni,
+			    "%s: min_switch %d > ticks_since_switch %d..",
+			    __func__, ticks - sample->sample_min_switch, san->ticks_since_switch[size_bin]);
+			change_rates = 1;
+		} else if ((! (ni->ni_flags & IEEE80211_NODE_HT)) &&
+		    (2*average_tx_time < san->stats[size_bin][san->current_rix[size_bin]].average_tx_time)) {
+			/* the current bit-rate is twice as slow as the best one */
+			IEEE80211_NOTE(vap,
+			    IEEE80211_MSG_RATECTL, &ni,
+			    "%s: 2x att (= %d) < cur_rix att %d",
+			    __func__,
+			    2 * average_tx_time, san->stats[size_bin][san->current_rix[size_bin]].average_tx_time);
+			change_rates = 1;
+		} else if ((ni->ni_flags & IEEE80211_NODE_HT)) {
+			int cur_rix = san->current_rix[size_bin];
+			int cur_att = san->stats[size_bin][cur_rix].average_tx_time;
+			/*
+			 * If the node is HT, upgrade it if the MCS rate is
+			 * higher and the average tx time is within 20% of
+			 * the current rate. It can fail a little.
+			 *
+			 * This is likely not optimal!
+			 */
+#if 0
+			printf("cur rix/att %x/%d, best rix/att %x/%d\n",
+			    MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time);
+#endif
+			if ((MCS(best_rix) > MCS(cur_rix)) &&
+			    (average_tx_time * 8) <= (cur_att * 10)) {
+				IEEE80211_NOTE(vap,
+				    IEEE80211_MSG_RATECTL, &ni,
+				    "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d",
+				    __func__,
+				    MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att);
+				change_rates = 1;
+			}
+		}
+
+		san->packets_since_sample[size_bin]++;
+		
+		if (change_rates) {
+			if (best_rix != san->current_rix[size_bin]) {
+				IEEE80211_NOTE(vap,
+				    IEEE80211_MSG_RATECTL,
+				    &ni,
+"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d",
+				    __func__,
+				    bin_to_size(size_bin),
+				    RATE(san->current_rix[size_bin]),
+				    san->stats[size_bin][san->current_rix[size_bin]].average_tx_time,
+				    san->stats[size_bin][san->current_rix[size_bin]].perfect_tx_time,
+				    RATE(best_rix),
+				    san->stats[size_bin][best_rix].average_tx_time,
+				    san->stats[size_bin][best_rix].perfect_tx_time,
+				    san->packets_since_switch[size_bin],
+				    mrr);
+			}
+			san->packets_since_switch[size_bin] = 0;
+			san->current_rix[size_bin] = best_rix;
+			san->ticks_since_switch[size_bin] = ticks;
+			/* 
+			 * Set the visible txrate for this node.
+			 */
+			ni->ni_txrate = dot11rate(rt, best_rix);
+		}
+		rix = san->current_rix[size_bin];
+		san->packets_since_switch[size_bin]++;
+	}
+	// *try0 = mrr ? san->sched[rix].t0 : ATH_TXMAXTRY;
+done:
+
+	/*
+	 * This bug totally sucks and should be fixed.
+	 *
+	 * For now though, let's not panic, so we can start to figure
+	 * out how to better reproduce it.
+	 */
+	if (rix < 0 || rix >= rt->rateCount) {
+		printf("%s: ERROR: rix %d out of bounds (rateCount=%d)\n",
+		    __func__,
+		    rix,
+		    rt->rateCount);
+		    rix = 0;	/* XXX just default for now */
+	}
+	KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix));
+
+	// *rix0 = rix;
+	// *txrate = rt->info[rix].rateCode
+	// 	| (shortPreamble ? rt->info[rix].shortPreamble : 0);
+	san->packets_sent[size_bin]++;
+
+	return rix;
+#undef DOT11RATE
+#undef MCS
+#undef RATE
+}
+
+static void
+sample_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
+{
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	uint8_t rix0 = sample_rate(ni, NULL, 0);
+	const struct txschedule *sched = &san->sched[rix0];
+	struct ieee80211_rc_series *rc = rc_info->iri_rc;
+
+	KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n",
+	    rix0, sched->r0));
+	/* XXX */
+	rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0;
+
+	rc[0].rix = sched->r0;
+	rc[1].rix = sched->r1;
+	rc[2].rix = sched->r2;
+	rc[3].rix = sched->r3;
+
+	rc[0].tries = sched->t0;
+	rc[1].tries = sched->t1;
+	rc[2].tries = sched->t2;
+	rc[3].tries = sched->t3;
+}
+
+static void
+update_stats(const struct ieee80211vap *vap,
+    	  const struct ieee80211_node *ni,
+		  int frame_size,
+		  int rix0, int tries0,
+		  int rix1, int tries1,
+		  int rix2, int tries2,
+		  int rix3, int tries3,
+		  int short_tries, int tries,
+		  int nframes, int nbad)
+{
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	struct ieee80211_sample *sample = san->san_sample;
+
+	const int size_bin = size_to_bin(frame_size);
+	const int size = bin_to_size(size_bin);
+
+	int is_ht40 = ieee80211_ratectl_hascap_cw40(vap, ni);
+	int tt, tries_so_far;
+	int pct;
+
+	if (!IS_RATE_DEFINED(san, rix0))
+		return;
+	tt = calc_usecs_unicast_packet(vap, size, rix0, short_tries,
+		MIN(tries0, tries) - 1, is_ht40);
+	tries_so_far = tries0;
+
+	if (tries1 && tries_so_far < tries) {
+		if (!IS_RATE_DEFINED(san, rix1))
+			return;
+		tt += calc_usecs_unicast_packet(vap, size, rix1, short_tries,
+			MIN(tries1 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
+		tries_so_far += tries1;
+	}
+
+	if (tries2 && tries_so_far < tries) {
+		if (!IS_RATE_DEFINED(san, rix2))
+			return;
+		tt += calc_usecs_unicast_packet(vap, size, rix2, short_tries,
+			MIN(tries2 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
+		tries_so_far += tries2;
+	}
+
+	if (tries3 && tries_so_far < tries) {
+		if (!IS_RATE_DEFINED(san, rix3))
+			return;
+		tt += calc_usecs_unicast_packet(vap, size, rix3, short_tries,
+			MIN(tries3 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
+	}
+
+	if (san->stats[size_bin][rix0].total_packets < sample->sanple_smoothing_minpackets) {
+		/* just average the first few packets */
+		int avg_tx = san->stats[size_bin][rix0].average_tx_time;
+		int packets = san->stats[size_bin][rix0].total_packets;
+		san->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes);
+	} else {
+		/* use a ewma */
+		san->stats[size_bin][rix0].average_tx_time = 
+			((san->stats[size_bin][rix0].average_tx_time * sample->sanple_smoothing_rate) + 
+			 (tt * (100 - sample->sanple_smoothing_rate))) / 100;
+	}
+	
+	/*
+	 * XXX Don't mark the higher bit rates as also having failed; as this
+	 * unfortunately stops those rates from being tasted when trying to
+	 * TX. This happens with 11n aggregation.
+	 */
+	if (nframes == nbad) {
+		san->stats[size_bin][rix0].successive_failures += nbad;
+
+	} else {
+		san->stats[size_bin][rix0].packets_acked += (nframes - nbad);
+		san->stats[size_bin][rix0].successive_failures = 0;
+	}
+	san->stats[size_bin][rix0].tries += tries;
+	san->stats[size_bin][rix0].last_tx = ticks;
+	san->stats[size_bin][rix0].total_packets += nframes;
+
+	/* update EWMA for this rix */
+
+	/* Calculate percentage based on current rate */
+	if (nframes == 0)
+		nframes = nbad = 1;
+	pct = ((nframes - nbad) * 1000) / nframes;
+
+	if (san->stats[size_bin][rix0].total_packets <
+	    sample->sanple_smoothing_minpackets) {
+		/* just average the first few packets */
+		int a_pct = (san->stats[size_bin][rix0].packets_acked * 1000) /
+		    (san->stats[size_bin][rix0].total_packets);
+		san->stats[size_bin][rix0].ewma_pct = a_pct;
+	} else {
+		/* use a ewma */
+		san->stats[size_bin][rix0].ewma_pct =
+			((san->stats[size_bin][rix0].ewma_pct * sample->sanple_smoothing_rate) +
+			 (pct * (100 - sample->sanple_smoothing_rate))) / 100;
+	}
+
+	if (rix0 == san->current_sample_rix[size_bin]) {
+		san->sample_tt[size_bin] = tt;
+		san->current_sample_rix[size_bin] = -1;
+	}
+}
+
+static void
+sample_tx_complete(const struct ieee80211vap *vap,
+    const struct ieee80211_node *ni, int ok,
+    void *arg1, void *arg2)
+{
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+
+	/* XXX need to change arg2 to pointer of ieee80211_rc_info */
+	struct ieee80211_rc_info *rc_info = (struct ieee80211_rc_info*)arg2;
+
+	int final_rix, short_tries, long_tries;
+	int nframes, nbad;
+	int frame_size, mrr;
+
+	/* update per vap statistics */
+	ieee80211_ratectl_update_stat(vap, rc_info);
+
+	final_rix = rt->rateCodeToIndex[rc_info->iri_txrate];
+	short_tries = rc_info->iri_shortretry;
+	/* XXX why plus 1 here? */
+	long_tries = rc_info->iri_longretry + 1;
+
+	nframes = rc_info->iri_txcnt;
+	nbad = rc_info->iri_failcnt;
+
+	frame_size = rc_info->iri_framelen;
+	mrr = 0;
+
+	if (nframes == 0) {
+		return;
+	}
+
+	if (frame_size == 0)		    /* NB: should not happen */
+		frame_size = 1500;
+
+	if (san->ratemask == 0) {
+		return;
+	}
+	
+	if (IEEE80211_RATECTL_HASCAP_MRR(vap))
+		mrr = 1;
+	/* XXX check HT protmode too */
+	if (mrr && !IEEE80211_RATECTL_HASCAP_MRRPROT(vap))
+		mrr = 0;
+	
+	if (!mrr || rc_info->iri_finaltsi == 0) {
+		if (!IS_RATE_DEFINED(san, final_rix)) {
+			return;
+		}
+		/*
+		 * Only one rate was used; optimize work.
+		 */
+		update_stats(vap, ni, frame_size, 
+			     final_rix, long_tries,
+			     0, 0,
+			     0, 0,
+			     0, 0,
+			     short_tries, long_tries,
+			     nframes, nbad);
+
+	} else {
+		int finalTSIdx = rc_info->iri_finaltsi;
+		int i;
+
+		/*
+		 * NB: series > 0 are not penalized for failure
+		 * based on the try counts under the assumption
+		 * that losses are often bursty and since we
+		 * sample higher rates 1 try at a time doing so
+		 * may unfairly penalize them.
+		 */
+		if (rc[0].tries) {
+			update_stats(vap, ni, frame_size,
+				     rc[0].rix, rc[0].tries,
+				     rc[1].rix, rc[1].tries,
+				     rc[2].rix, rc[2].tries,
+				     rc[3].rix, rc[3].tries,
+				     short_tries, long_tries,
+				     nframes, nbad);
+			long_tries -= rc[0].tries;
+		}
+		
+		if (rc[1].tries && finalTSIdx > 0) {
+			update_stats(vap, ni, frame_size,
+				     rc[1].rix, rc[1].tries,
+				     rc[2].rix, rc[2].tries,
+				     rc[3].rix, rc[3].tries,
+				     0, 0,
+				     short_tries, long_tries,
+				     nframes, nbad);
+			long_tries -= rc[1].tries;
+		}
+
+		if (rc[2].tries && finalTSIdx > 1) {
+			update_stats(vap, ni, frame_size,
+				     rc[2].rix, rc[2].tries,
+				     rc[3].rix, rc[3].tries,
+				     0, 0,
+				     0, 0,
+				     short_tries, long_tries,
+				     nframes, nbad);
+			long_tries -= rc[2].tries;
+		}
+
+		if (rc[3].tries && finalTSIdx > 2) {
+			update_stats(vap, ni, frame_size,
+				     rc[3].rix, rc[3].tries,
+				     0, 0,
+				     0, 0,
+				     0, 0,
+				     short_tries, long_tries,
+				     nframes, nbad);
+		}
+	}
+}
+
+static void
+sample_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni,
+    void *arg1, void *arg2, void *arg3)
+{
+	/* nothing here. */
+}
+
+static void
+sample_setinterval(const struct ieee80211vap *vap, int msecs)
+{
+	struct ieee80211_sample *sample = vap->iv_rs;
+	int t;
+
+	if (msecs < 100)
+		msecs = 100;
+	t = msecs_to_ticks(msecs);
+	/* ieee80211_sample doesn't have the sample_interval field by now */
+	// sample->sample_interval = (t < 1) ? 1 : t;
+}
+
+static void
+sample_stats(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211vap *vap = ni->ni_vap;
+	struct ieee80211_sample_node *san = ni->ni_rctls;
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	uint64_t mask;
+	int rix, y;
+
+	printf("\n[%s] refcnt %d static_rix (%d %s) ratemask 0x%jx\n",
+	    ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni),
+	    dot11rate(rt, san->static_rix),
+	    dot11rate_label(rt, san->static_rix),
+	    (uintmax_t)san->ratemask);
+	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+		printf("[%4u] cur rix %d (%d %s) since switch: packets %d ticks %u\n",
+		    bin_to_size(y), san->current_rix[y],
+		    dot11rate(rt, san->current_rix[y]),
+		    dot11rate_label(rt, san->current_rix[y]),
+		    san->packets_since_switch[y], san->ticks_since_switch[y]);
+		printf("[%4u] last sample (%d %s) cur sample (%d %s) packets sent %d\n",
+		    bin_to_size(y),
+		    dot11rate(rt, san->last_sample_rix[y]),
+		    dot11rate_label(rt, san->last_sample_rix[y]),
+		    dot11rate(rt, san->current_sample_rix[y]),
+		    dot11rate_label(rt, san->current_sample_rix[y]),
+		    san->packets_sent[y]);
+		printf("[%4u] packets since sample %d sample tt %u\n",
+		    bin_to_size(y), san->packets_since_sample[y],
+		    san->sample_tt[y]);
+	}
+	for (mask = san->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
+		if ((mask & 1) == 0)
+				continue;
+		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+			if (san->stats[y][rix].total_packets == 0)
+				continue;
+			printf("[%2u %s:%4u] %8ju:%-8ju (%3d%%) (EWMA %3d.%1d%%) T %8ju F %4d avg %5u last %u\n",
+			    dot11rate(rt, rix), dot11rate_label(rt, rix),
+			    bin_to_size(y),
+			    (uintmax_t) san->stats[y][rix].total_packets,
+			    (uintmax_t) san->stats[y][rix].packets_acked,
+			    (int) ((san->stats[y][rix].packets_acked * 100ULL) /
+			     san->stats[y][rix].total_packets),
+			    san->stats[y][rix].ewma_pct / 10,
+			    san->stats[y][rix].ewma_pct % 10,
+			    (uintmax_t) san->stats[y][rix].tries,
+			    san->stats[y][rix].successive_failures,
+			    san->stats[y][rix].average_tx_time,
+			    ticks - san->stats[y][rix].last_tx);
+		}
+	}
+}
+
+static int
+sample_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+	struct ieee80211vap *vap = arg1;
+	struct ieee80211com *ic = vap->iv_ifp->if_l2com;
+	int error, v;
+
+	v = 0;
+	error = sysctl_handle_int(oidp, &v, 0, req);
+	if (error || !req->newptr)
+		return error;
+	ieee80211_iterate_nodes(&ic->ic_sta, sample_stats, NULL);
+	return 0;
+}
+
+static int
+sample_sysctl_smoothing_rate(SYSCTL_HANDLER_ARGS)
+{
+	struct ieee80211vap *vap = arg1;
+	struct ieee80211_sample *sample = vap->iv_rs;
+	int rate, error;
+
+	rate = sample->sample_smoothing_rate;
+	error = sysctl_handle_int(oidp, &rate, 0, req);
+	if (error || !req->newptr)
+		return error;
+	if (!(0 <= rate && rate < 100))
+		return EINVAL;
+	sample->sample_smoothing_rate = rate;
+	sample->sample_smoothing_minpackets = 100 / (100 - rate);
+	return 0;
+}
+
+static int
+sample_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
+{
+	struct ieee80211vap *vap = arg1;
+	struct ieee80211_sample *sample = vap->iv_rs;
+	int rate, error;
+
+	rate = sample->sample_rate;
+	error = sysctl_handle_int(oidp, &rate, 0, req);
+	if (error || !req->newptr)
+		return error;
+	if (!(2 <= rate && rate <= 100))
+		return EINVAL;
+	sample->sample_rate = rate;
+	return 0;
+}
+
+static void
+sample_sysctlattach(struct ieee80211vap *vap,
+    struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+	    "sample_smoothing_rate", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+	    sample_sysctl_smoothing_rate, "I",
+	    "sample: smoothing rate for avg tx time (%%)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+	    "sample_rate", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+	    sample_sysctl_sample_rate, "I",
+	    "sample: percent air time devoted to sampling new rates (%%)");
+	/* XXX max_successive_failures, stale_failure_timeout, min_switch */
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+	    "sample_stats", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+	    sample_sysctl_stats, "I", "sample: print statistics");
+}
Index: sys/net80211/ieee80211_rc_sample.h
===================================================================
--- sys/net80211/ieee80211_rc_sample.h	(revision 0)
+++ sys/net80211/ieee80211_rc_sample.h	(revision 256474)
@@ -0,0 +1,287 @@
+/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.h 240382 2012-08-15 07:10:10Z adrian $*/
+
+/*-
+ * Copyright (c) 2005 John Bicket
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef _NET80211_IEEE80211_RATECTL_SAMPLE_H_
+#define _NET80211_IEEE80211_RATECTL_SAMPLE_H_
+
+#include <net80211/ieee80211_ratectl.h>
+
+/*
+ * for now, we track performance for three different packet
+ * size buckets
+ */
+#define NUM_PACKET_SIZE_BINS 2
+#define	SAMPLE_MAXRATES	64		/* NB: corresponds to hal info[32] */
+
+/*
+ * Rate control settings.
+ */
+struct ieee80211_sample {
+	struct ieee80211_rc_stat sample_stat;
+	int	sample_smoothing_rate;			/* ewma percentage [0..99] */
+	int	sample_smoothing_minpackets;
+	int	sample_rate;			/* %time to try different tx rates */
+	int	sample_max_successive_failures;
+	int	sample_stale_failure_timeout;	/* how long to honor max_successive_failures */
+	int	sample_min_switch;		/* min time between rate changes */
+	int	sample_min_good_pct;	/* min good percentage for a rate to be considered */
+};
+
+struct rate_stats {	
+	unsigned average_tx_time;
+	int successive_failures;
+	uint64_t tries;
+	uint64_t total_packets;	/* pkts total since assoc */
+	uint64_t packets_acked;	/* pkts acked since assoc */
+	int ewma_pct;	/* EWMA percentage */
+	unsigned perfect_tx_time; /* transmit time for 0 retries */
+	int last_tx;
+};
+
+struct txschedule {
+	uint8_t	t0, r0;		/* series 0: tries, rate code */
+	uint8_t	t1, r1;		/* series 1: tries, rate code */
+	uint8_t	t2, r2;		/* series 2: tries, rate code */
+	uint8_t	t3, r3;		/* series 3: tries, rate code */
+};
+
+/*
+ * Rate control state for a given node.
+ */
+/* XXX change naming conversion? */
+struct ieee80211_sample_node {
+	struct ieee80211_sample *san_sample;/* backpointer */
+	int static_rix;			/* rate index of fixed tx rate */
+	uint64_t ratemask;		/* bit mask of valid rate indices */
+	const struct txschedule *sched;	/* tx schedule table */
+
+	struct rate_stats stats[NUM_PACKET_SIZE_BINS][SAMPLE_MAXRATES];
+	int last_sample_rix[NUM_PACKET_SIZE_BINS];
+
+	int current_sample_rix[NUM_PACKET_SIZE_BINS];       
+	int packets_sent[NUM_PACKET_SIZE_BINS];
+
+	int current_rix[NUM_PACKET_SIZE_BINS];
+	int packets_since_switch[NUM_PACKET_SIZE_BINS];
+	unsigned ticks_since_switch[NUM_PACKET_SIZE_BINS];
+
+	int packets_since_sample[NUM_PACKET_SIZE_BINS];
+	unsigned sample_tt[NUM_PACKET_SIZE_BINS];
+};
+
+#define	IS_RATE_DEFINED(san, rix)	(((san)->ratemask & (1<<(rix))) != 0)
+
+#ifndef MIN
+#define	MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define	MAX(a,b)	((a) > (b) ? (a) : (b))
+#endif
+
+static const int packet_size_bins[NUM_PACKET_SIZE_BINS]  = { 250, 1600 };
+
+static inline int
+bin_to_size(int index)
+{
+	return packet_size_bins[index];
+}
+
+static inline int
+size_to_bin(int size) 
+{
+#if NUM_PACKET_SIZE_BINS > 1
+	if (size <= packet_size_bins[0])
+		return 0;
+#endif
+#if NUM_PACKET_SIZE_BINS > 2
+	if (size <= packet_size_bins[1])
+		return 1;
+#endif
+#if NUM_PACKET_SIZE_BINS > 3
+	if (size <= packet_size_bins[2])
+		return 2;
+#endif
+#if NUM_PACKET_SIZE_BINS > 4
+#error "add support for more packet sizes"
+#endif
+	return NUM_PACKET_SIZE_BINS-1;
+}
+
+static uint32_t sample_pkt_txtime(const struct ieee80211_rate_table *rt,
+	uint32_t frameLen, uint16_t rateix, int isht40, int isShortPreamble)
+{
+	uint8_t rc;
+    int numStreams;
+
+    rc = rt->info[rateix].rateCode;
+
+    /* Legacy rate? Return the old way */
+    if (! IS_HT_RATE(rc))
+    	return ieee80211_compute_duration(rt, frameLen, rateix, isShortPreamble);
+
+    /* 11n frame - extract out the number of spatial streams */
+    numStreams = HT_RC_2_STREAMS(rc);
+    KASSERT(numStreams > 0 && numStreams <= 4,
+        ("number of spatial streams needs to be 1..3: MCS rate 0x%x!",
+        rateix));
+
+    return ieee80211_compute_duration_ht(frameLen, rc, numStreams, isht40, isShortPreamble);
+}
+
+#define WIFI_CW_MIN 31
+#define WIFI_CW_MAX 1023
+
+/*
+ * Calculate the transmit duration of a frame.
+ */
+static unsigned calc_usecs_unicast_packet(const struct ieee80211vap *vap,
+				int length,
+				int rix, int short_retries,
+				int long_retries, int is_ht40)
+{
+	const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+	struct ieee80211com *ic = vap->iv_ic;
+	int curmode = ieee80211_chan2mode(vap->iv_ic->ic_curchan);
+	
+	unsigned t_slot, t_difs, t_sifs; 
+	int rts, cts;
+	int tt, x, cw, cix;
+
+	int tt = 0;
+	int x = 0;
+	int cw = WIFI_CW_MIN;
+
+	KASSERT(rt != NULL, ("no rate table, mode %u", curmode));
+
+	if (rix >= rt->rateCount) {
+		printf("bogus rix %d, max %u, mode %u\n",
+		       rix, rt->rateCount, curmode);
+		return 0;
+	}
+	cix = rt->info[rix].controlRate;
+	/* 
+	 * XXX getting mac/phy level timings should be fixed for turbo
+	 * rates, and there is probably a way to get this from the
+	 * hal...
+	 */
+	switch (rt->info[rix].phy) {
+	case IEEE80211_T_OFDM:
+		t_slot = 9;
+		t_sifs = 16;
+		t_difs = 28;
+		/* fall through */
+	case IEEE80211_T_TURBO:
+		t_slot = 9;
+		t_sifs = 8;
+		t_difs = 28;
+		break;
+	case IEEE80211_T_HT:
+		t_slot = 9;
+		t_sifs = 8;
+		t_difs = 28;
+		break;
+	case IEEE80211_T_DS:
+		/* fall through to default */
+	default:
+		/* pg 205 ieee.802.11.pdf */
+		t_slot = 20;
+		t_difs = 50;
+		t_sifs = 10;
+	}
+
+	rts = cts = 0;
+
+	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+	    rt->info[rix].phy == IEEE80211_T_OFDM) {
+		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+			rts = 1;
+		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+			cts = 1;
+
+		int protrix;
+    	if (curmode == IEEE80211_MODE_11G)
+    	    protrix = rt->rateCodeToIndex[2*2];
+    	else 
+    	    protrix = rt->rateCodeToIndex[2*1];
+    	if (0xff == protrix)
+    		protrix = 0;
+
+		cix = rt->info[protrix].controlRate;
+	}
+
+	if (0 /*length > ic->ic_rtsthreshold */) {
+		rts = 1;
+	}
+
+	if (rts || cts) {
+		int ctsrate;
+		int ctsduration = 0;
+
+		/* NB: this is intentionally not a runtime check */
+		KASSERT(cix < rt->rateCount,
+		    ("bogus cix %d, max %u, mode %u\n", cix, rt->rateCount,
+		     curmode));
+
+		ctsrate = rt->info[cix].rateCode | rt->info[cix].shortPreamble;
+		if (rts)		/* SIFS + CTS */
+			ctsduration += rt->info[cix].spAckDuration;
+
+		/* XXX assumes short preamble */
+		ctsduration += sample_pkt_txtime(rt, length, rix, is_ht40, 0);
+
+		if (cts)	/* SIFS + ACK */
+			ctsduration += rt->info[cix].spAckDuration;
+
+		tt += (short_retries + 1) * ctsduration;
+	}
+	tt += t_difs;
+
+	/* XXX assumes short preamble */
+	tt += (long_retries+1)*sample_pkt_txtime(rt, length, rix, is_ht40, 0);
+
+	tt += (long_retries+1)*(t_sifs + rt->info[rix].spAckDuration);
+
+	for (x = 0; x <= short_retries + long_retries; x++) {
+		cw = MIN(WIFI_CW_MAX, (cw + 1) * 2);
+		tt += (t_slot * cw/2);
+	}
+	return tt;
+}
+
+#endif /* _NET80211_IEEE80211_RATECTL_SAMPLE_H_ */
Index: sys/net80211/ieee80211_rc_sample_txsched.h
===================================================================
--- sys/net80211/ieee80211_rc_sample_txsched.h	(revision 0)
+++ sys/net80211/ieee80211_rc_sample_txsched.h	(revision 256491)
@@ -0,0 +1,241 @@
+/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/tx_schedules.h 240384 2012-08-15 07:50:42Z adrian $*/
+
+/*-
+ * Copyright (c) 2005 John Bicket
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+#ifndef	_NET80211_IEEE80211_RATECTL_SAMPLE_TXSCHEDULES_H_
+#define	_NET80211_IEEE80211_RATECTL_SAMPLE_TXSCHEDULES_H_
+
+#include <sys/cdefs.h>
+
+#define A(_r) \
+    (((_r) == 6)   ? 0 : (((_r) == 9)   ? 1 : (((_r) == 12)  ? 2 : \
+    (((_r) == 18)  ? 3 : (((_r) == 24)  ? 4 : (((_r) == 36)  ? 5 : \
+    (((_r) == 48)  ? 6 : (((_r) == 54)  ? 7 : 0))))))))
+static const struct txschedule series_11a[] = {
+	{ 3,A( 6), 3,A(  6), 0,A(  6), 0,A( 6) },	/*   6Mb/s */
+	{ 4,A( 9), 3,A(  6), 4,A(  6), 0,A( 6) },	/*   9Mb/s */
+	{ 4,A(12), 3,A(  6), 4,A(  6), 0,A( 6) },	/*  12Mb/s */
+	{ 4,A(18), 3,A( 12), 4,A(  6), 2,A( 6) },	/*  18Mb/s */
+	{ 4,A(24), 3,A( 18), 4,A( 12), 2,A( 6) },	/*  24Mb/s */
+	{ 4,A(36), 3,A( 24), 4,A( 18), 2,A( 6) },	/*  36Mb/s */
+	{ 4,A(48), 3,A( 36), 4,A( 24), 2,A(12) },	/*  48Mb/s */
+	{ 4,A(54), 3,A( 48), 4,A( 36), 2,A(24) }	/*  54Mb/s */
+};
+
+#define NA1(_r) \
+	(((_r) == 6.5)  ? 8 : (((_r) == 13)  ?  9 : (((_r) == 19.5)? 10 : \
+	(((_r) == 26)  ? 11 : (((_r) == 39)  ? 12 : (((_r) == 52)  ? 13 : \
+	(((_r) == 58.5)? 14 : (((_r) == 65)  ? 15 : 0))))))))
+#define NA2(_r) \
+	(((_r) == 13) ? 16 : (((_r) == 26) ? 17 : (((_r) == 39) ? 18 : \
+	(((_r) == 52) ? 19 : (((_r) == 78) ? 20 : (((_r) == 104)? 21 : \
+	(((_r) == 117)? 22 : (((_r) == 130)? 23 : 0))))))))
+#define NA3(_r) \
+	(((_r) == 19.5)  ? 24 : (((_r) == 39) ? 25 : (((_r) == 58.5)  ? 26 : \
+	(((_r) == 78)  ? 27 : (((_r) == 117) ? 28 : (((_r) == 156) ? 29 : \
+	(((_r) == 175.5) ? 30 : (((_r) == 195)? 31 : 0))))))))
+static const struct txschedule series_11na[] = {
+	{ 3,A( 6), 3,A(  6), 0,A(  6), 0,A( 6) },       /*   6Mb/s */
+	{ 4,A( 9), 3,A(  6), 4,A(  6), 0,A( 6) },       /*   9Mb/s */
+	{ 4,A(12), 3,A(  6), 4,A(  6), 0,A( 6) },       /*  12Mb/s */
+	{ 4,A(18), 3,A( 12), 4,A(  6), 2,A( 6) },       /*  18Mb/s */
+	{ 4,A(24), 3,A( 18), 4,A( 12), 2,A( 6) },       /*  24Mb/s */
+	{ 4,A(36), 3,A( 24), 4,A( 18), 2,A( 6) },       /*  36Mb/s */
+	{ 4,A(48), 3,A( 36), 4,A( 24), 2,A(12) },       /*  48Mb/s */
+	{ 4,A(54), 3,A( 48), 4,A( 36), 2,A(24) },       /*  54Mb/s */
+
+	/* 1 stream rates */
+
+	{ 3,NA1( 6.5), 3,NA1( 6.5), 0,NA1( 6.5), 0,NA1(6.5) },  /* 6.5Mb/s */
+	{ 4,NA1(  13), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) },  /*  13Mb/s */
+	{ 4,NA1(19.5), 3,NA1( 6.5), 4,NA1( 6.5), 0,NA1(6.5) },  /*19.5Mb/s */
+	{ 4,NA1(  26), 3,NA1(19.5), 4,NA1( 6.5), 2,NA1(6.5) },  /*  26Mb/s */
+	{ 4,NA1(  39), 3,NA1(  26), 4,NA1(19.5), 2,NA1(6.5) },  /*  39Mb/s */
+	{ 4,NA1(  52), 3,NA1(  39), 4,NA1(  26), 2,NA1(6.5) },  /*  52Mb/s */
+	{ 4,NA1(58.5), 3,NA1(  52), 4,NA1(  39), 2,NA1( 13) },  /*58.5Mb/s */
+	{ 4,NA1(  65), 3,NA1(58.5), 4,NA1(  52), 2,NA1( 13) },  /*  65Mb/s */
+
+	/* 2 stream rates */
+
+	{ 3,NA2(  13), 3,NA2(  13), 0,NA2(  13), 0,NA2( 13) },  /*  13Mb/s */
+	{ 4,NA2(  26), 3,NA2(  13), 4,NA2(  13), 0,NA2( 13) },  /*  26Mb/s */
+	{ 4,NA2(  39), 3,NA2(  26), 4,NA2(  13), 2,NA2( 13) },  /*  39Mb/s */
+	{ 4,NA2(  52), 3,NA2(  39), 4,NA2(  26), 2,NA2( 13) },  /*  52Mb/s */
+	{ 4,NA2(  78), 3,NA2(  52), 4,NA2(  39), 2,NA2( 13) },  /*  78Mb/s */
+	{ 4,NA2( 104), 3,NA2(  78), 4,NA2(  52), 2,NA2( 13) },  /* 104Mb/s */
+	{ 4,NA2( 117), 3,NA2( 104), 4,NA2(  78), 2,NA2( 26) },  /* 117Mb/s */
+	{ 4,NA2( 130), 3,NA2( 117), 4,NA2( 104), 2,NA2( 26) },   /* 130Mb/s */
+
+	/* 3 stream rates */
+
+	{ 3,NA3(19.5), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) },  /*  19Mb/s */
+	{ 3,NA3(  39), 3,NA3(19.5), 0,NA3(19.5), 0,NA3(19.5) },  /*  39Mb/s */
+	{ 3,NA3(58.5), 3,NA3(  39), 0,NA3(19.5), 0,NA3(19.5) },  /*  58Mb/s */
+	{ 3,NA3(  78), 3,NA3(58.5), 0,NA3(  39), 0,NA3(19.5) },  /*  78Mb/s */
+	{ 3,NA3( 117), 3,NA3(  78), 0,NA3(58.5), 0,NA3(19.5) },  /* 117Mb/s */
+	{ 3,NA3( 156), 3,NA3( 117), 0,NA3(  78), 0,NA3(19.5) },  /*  156Mb/s */
+	{ 3,NA3(175.5), 3,NA3( 156), 0,NA3( 117), 0,NA3(  39) },  /*  175Mb/s */
+	{ 3,NA3( 195), 3,NA3( 195), 0,NA3( 156), 0,NA3(58.5) },  /* 195Mb/s */
+};
+#undef A
+#undef NA3
+#undef NA2
+#undef NA1
+
+#define G(_r) \
+    (((_r) == 1)   ? 0 : (((_r) == 2)   ? 1 : (((_r) == 5.5) ? 2 : \
+    (((_r) == 11)  ? 3 : (((_r) == 6)   ? 4 : (((_r) == 9)   ? 5 : \
+    (((_r) == 12)  ? 6 : (((_r) == 18)  ? 7 : (((_r) == 24)  ? 8 : \
+    (((_r) == 36)  ? 9 : (((_r) == 48)  ? 10 : (((_r) == 54)  ? 11 : 0))))))))))))
+static const struct txschedule series_11g[] = {
+	{ 3,G( 1), 3,G(  1), 0,G(  1), 0,G( 1) },	/*   1Mb/s */
+	{ 4,G( 2), 3,G(  1), 4,G(  1), 0,G( 1) },	/*   2Mb/s */
+	{ 4,G(5.5),3,G(  2), 4,G(  1), 2,G( 1) },	/* 5.5Mb/s */
+	{ 4,G(11), 3,G(5.5), 4,G(  2), 2,G( 1) },	/*  11Mb/s */
+	{ 4,G( 6), 3,G(5.5), 4,G(  2), 2,G( 1) },	/*   6Mb/s */
+	{ 4,G( 9), 3,G(  6), 4,G(5.5), 2,G( 1) },	/*   9Mb/s */
+	{ 4,G(12), 3,G( 11), 4,G(5.5), 2,G( 1) },	/*  12Mb/s */
+	{ 4,G(18), 3,G( 12), 4,G( 11), 2,G( 1) },	/*  18Mb/s */
+	{ 4,G(24), 3,G( 18), 4,G( 12), 2,G( 1) },	/*  24Mb/s */
+	{ 4,G(36), 3,G( 24), 4,G( 18), 2,G( 1) },	/*  36Mb/s */
+	{ 4,G(48), 3,G( 36), 4,G( 24), 2,G( 1) },	/*  48Mb/s */
+	{ 4,G(54), 3,G( 48), 4,G( 36), 2,G( 1) }	/*  54Mb/s */
+};
+
+#define NG1(_r) \
+	(((_r) == 6.5) ? 12 : (((_r) == 13) ? 13 : (((_r) == 19.5)? 14 : \
+	(((_r) == 26)  ? 15 : (((_r) == 39) ? 16 : (((_r) == 52)  ? 17 : \
+	(((_r) == 58.5)? 18 : (((_r) == 65) ? 19 : 0))))))))
+#define NG2(_r) \
+	(((_r) == 13)  ? 20 : (((_r) == 26) ? 21 : (((_r) == 39)  ? 22 : \
+	(((_r) == 52)  ? 23 : (((_r) == 78) ? 24 : (((_r) == 104) ? 25 : \
+	(((_r) == 117) ? 26 : (((_r) == 130)? 27 : 0))))))))
+#define NG3(_r) \
+	(((_r) == 19.5)  ? 28 : (((_r) == 39) ? 29 : (((_r) == 58.5)  ? 30 : \
+	(((_r) == 78)  ? 31 : (((_r) == 117) ? 32 : (((_r) == 156) ? 33 : \
+	(((_r) == 175.5) ? 34 : (((_r) == 195)? 35 : 0))))))))
+
+static const struct txschedule series_11ng[] = {
+	{ 3,G( 1), 3,G(  1), 0,G(  1), 0,G( 1) },       /*   1Mb/s */
+	{ 4,G( 2), 3,G(  1), 4,G(  1), 0,G( 1) },       /*   2Mb/s */
+	{ 4,G(5.5),3,G(  2), 4,G(  1), 2,G( 1) },       /* 5.5Mb/s */
+	{ 4,G(11), 3,G(5.5), 4,G(  2), 2,G( 1) },       /*  11Mb/s */
+	{ 4,G( 6), 3,G(5.5), 4,G(  2), 2,G( 1) },       /*   6Mb/s */
+	{ 4,G( 9), 3,G(  6), 4,G(5.5), 2,G( 1) },       /*   9Mb/s */
+	{ 4,G(12), 3,G( 11), 4,G(5.5), 2,G( 1) },       /*  12Mb/s */
+	{ 4,G(18), 3,G( 12), 4,G( 11), 2,G( 1) },       /*  18Mb/s */
+	{ 4,G(24), 3,G( 18), 4,G( 12), 2,G( 1) },       /*  24Mb/s */
+	{ 4,G(36), 3,G( 24), 4,G( 18), 2,G( 1) },       /*  36Mb/s */
+	{ 4,G(48), 3,G( 36), 4,G( 24), 2,G( 1) },       /*  48Mb/s */
+	{ 4,G(54), 3,G( 48), 4,G( 36), 2,G( 1) },       /*  54Mb/s */
+
+	/* 1 stream rates */
+
+	{ 3,NG1( 6.5), 3,NG1( 6.5), 0,NG1( 6.5), 0,NG1(6.5) },  /* 6.5Mb/s */
+	{ 4,NG1(  13), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) },  /*  13Mb/s */
+	{ 4,NG1(19.5), 3,NG1( 6.5), 4,NG1( 6.5), 0,NG1(6.5) },  /*19.5Mb/s */
+	{ 4,NG1(  26), 3,NG1(19.5), 4,NG1( 6.5), 2,NG1(6.5) },  /*  26Mb/s */
+	{ 4,NG1(  39), 3,NG1(  26), 4,NG1(19.5), 2,NG1(6.5) },  /*  39Mb/s */
+	{ 4,NG1(  52), 3,NG1(  39), 4,NG1(  26), 2,NG1(6.5) },  /*  52Mb/s */
+	{ 4,NG1(58.5), 3,NG1(  52), 4,NG1(  39), 2,NG1( 13) },  /*58.5Mb/s */
+	{ 4,NG1(  65), 3,NG1(58.5), 4,NG1(  52), 2,NG1( 13) },  /*  65Mb/s */
+
+	/* 2 stream rates */
+
+	{ 3,NG2(  13), 3,NG2(  13), 0,NG2(  13), 0,NG2( 13) },  /*  13Mb/s */
+	{ 4,NG2(  26), 3,NG2(  13), 4,NG2(  13), 0,NG2( 13) },  /*  26Mb/s */
+	{ 4,NG2(  39), 3,NG2(  26), 4,NG2(  13), 2,NG2( 13) },  /*  39Mb/s */
+	{ 4,NG2(  52), 3,NG2(  39), 4,NG2(  26), 2,NG2( 13) },  /*  52Mb/s */
+	{ 4,NG2(  78), 3,NG2(  52), 4,NG2(  39), 2,NG2( 13) },  /*  78Mb/s */
+	{ 4,NG2( 104), 3,NG2(  78), 4,NG2(  52), 2,NG2( 13) },  /* 104Mb/s */
+	{ 4,NG2( 117), 3,NG2( 104), 4,NG2(  78), 2,NG2( 26) },  /* 117Mb/s */
+	{ 4,NG2( 130), 3,NG2( 117), 4,NG2( 104), 2,NG2( 26) },  /* 130Mb/s */
+
+	/* 3 stream rates */
+
+	{ 3,NG3(19.5), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) },  /*  19Mb/s */
+	{ 3,NG3(  39), 3,NG3(19.5), 0,NG3(19.5), 0,NG3(19.5) },  /*  39Mb/s */
+	{ 3,NG3(58.5), 3,NG3(  39), 0,NG3(19.5), 0,NG3(19.5) },  /*  58Mb/s */
+	{ 3,NG3(  78), 3,NG3(58.5), 0,NG3(  39), 0,NG3(19.5) },  /*  78Mb/s */
+	{ 3,NG3( 117), 3,NG3(  78), 0,NG3(58.5), 0,NG3(19.5) },  /* 117Mb/s */
+	{ 3,NG3( 156), 3,NG3( 117), 0,NG3(  78), 0,NG3(19.5) },  /*  156Mb/s */
+	{ 3,NG3(175.5), 3,NG3( 156), 0,NG3( 117), 0,NG3(  39) },  /*  175Mb/s */
+	{ 3,NG3( 195), 3,NG3( 195), 0,NG3( 156), 0,NG3(58.5) },  /* 195Mb/s */
+
+};
+#undef G
+#undef NG3
+#undef NG2
+#undef NG1
+
+#define H(_r) \
+    (((_r) == 3)   ? 0 : (((_r) == 4.5) ? 1 : (((_r) == 6)  ? 2 : \
+    (((_r) == 9)   ? 3 : (((_r) == 12)  ? 4 : (((_r) == 18) ? 5 : \
+    (((_r) == 24)  ? 6 : (((_r) == 27)  ? 7 : 0))))))))
+static const struct txschedule series_half[] = {
+	{ 3,H( 3), 3,H(  3), 0,H(  3), 0,H( 3) },	/*   3Mb/s */
+	{ 4,H(4.5),3,H(  3), 4,H(  3), 0,H( 3) },	/* 4.5Mb/s */
+	{ 4,H( 6), 3,H(  3), 4,H(  3), 0,H( 3) },	/*   6Mb/s */
+	{ 4,H( 9), 3,H(  6), 4,H(  3), 2,H( 3) },	/*   9Mb/s */
+	{ 4,H(12), 3,H(  9), 4,H(  6), 2,H( 3) },	/*  12Mb/s */
+	{ 4,H(18), 3,H( 12), 4,H(  9), 2,H( 3) },	/*  18Mb/s */
+	{ 4,H(24), 3,H( 18), 4,H( 12), 2,H( 6) },	/*  24Mb/s */
+	{ 4,H(27), 3,H( 24), 4,H( 18), 2,H(12) }	/*  27Mb/s */
+};
+#undef H
+
+#ifdef Q
+#undef Q
+#endif
+#define Q(_r) \
+    (((_r) == 1.5) ? 0 : (((_r) ==2.25) ? 1 : (((_r) == 3)  ? 2 : \
+    (((_r) == 4.5) ? 3 : (((_r) ==  6)  ? 4 : (((_r) == 9)  ? 5 : \
+    (((_r) == 12)  ? 6 : (((_r) == 13.5)? 7 : 0))))))))
+static const struct txschedule series_quarter[] = {
+	{ 3,Q( 1.5),3,Q(1.5), 0,Q(1.5), 0,Q(1.5) },	/* 1.5Mb/s */
+	{ 4,Q(2.25),3,Q(1.5), 4,Q(1.5), 0,Q(1.5) },	/*2.25Mb/s */
+	{ 4,Q(   3),3,Q(1.5), 4,Q(1.5), 0,Q(1.5) },	/*   3Mb/s */
+	{ 4,Q( 4.5),3,Q(  3), 4,Q(1.5), 2,Q(1.5) },	/* 4.5Mb/s */
+	{ 4,Q(   6),3,Q(4.5), 4,Q(  3), 2,Q(1.5) },	/*   6Mb/s */
+	{ 4,Q(   9),3,Q(  6), 4,Q(4.5), 2,Q(1.5) },	/*   9Mb/s */
+	{ 4,Q(  12),3,Q(  9), 4,Q(  6), 2,Q(  3) },	/*  12Mb/s */
+	{ 4,Q(13.5),3,Q( 12), 4,Q(  9), 2,Q(  6) }	/*13.5Mb/s */
+};
+#undef Q
+
+#endif /* _NET80211_IEEE80211_RATECTL_SAMPLE_TXSCHEDULES_H_ */

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFnsE3eMrwpo=hcFb9XfpLL53Ppso%2B%2BXTBfpieP8FGejAKW1_w>