Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 15 Sep 2013 12:08:37 +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:  <CAFnsE3fnApV_=FNggEuhy1B=idXLxgymqqmJ=9XXrjspLoe%2BWA@mail.gmail.com>
In-Reply-To: <CAJ-Vmonbj9KE_oM6xXPnKksA1u=9FH41e4_bQE1cgwebydZgtA@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> <CAFnsE3eMrwpo=hcFb9XfpLL53Ppso%2B%2BXTBfpieP8FGejAKW1_w@mail.gmail.com> <CAJ-Vmo=s=0u7VO00vgzxmR7bE4aTtMrshZ3j5F312dya284V1g@mail.gmail.com> <CAFnsE3c%2BOg_2hVXO3zU7gmY4xFQjnxbv=ehJm=pbbpLJcgwJrQ@mail.gmail.com> <CAJ-Vmo=D3BNhhSn9D3x6hBry=fDecOd7BwttQ5Yw2iUKGDBN3w@mail.gmail.com> <CAFnsE3dw5bOyVRqMb9K3ME%2BHoDL61-e_XNJjjt_nRTaY=4dnhw@mail.gmail.com> <CAJ-Vmo=0tkTKQnOjrmYKqWqC%2B_nnFLqimdp06STETxLsFgxKxQ@mail.gmail.com> <CAFnsE3dm27483GVp4PRiwk=sxagT7NXQz4fmyOpth2zpCM2MwQ@mail.gmail.com> <CAFnsE3fvpjCXEC1gN46VXfPdeXG5RSVzNiJkZJiQ6xSYQFb89g@mail.gmail.com> <CAFnsE3e1r6Ah5SSrqnrxoqXMRm3yCaUxCjSD90epWQJEZU-pzw@mail.gmail.com> <CAJ-Vmok3dAHiwbeyEU6B4oWm=n36ME2yyGE69twjdEygr8iZSw@mail.gmail.com> <CAFnsE3dxXr05%2Bv7F2hF7Qauoz-qqDzxkXiZrhUAtaxf0ObNhDg@mail.gmail.com> <CAJ-Vmo=Se2JziV8D2PYNgxmZro-3JRpp3ZsRkTTWvUQ0YigRPw@mail.gmail.com> <CAFnsE3cFFHxOwLY2OFxhCUAM-Bg2zZvjrC8LMx17N-dfND74KQ@mail.gmail.com> <CAJ-Vmonbj9KE_oM6xXPnKksA1u=9FH41e4_bQE1cgwebydZgtA@mail.gmail.com>

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

[-- Attachment #1 --]
Hi!

Yes, a call to ieee80211_ratectl_rc_info_set() is needed. To make other
drivers work, the __init__ and __findrate__ parts also need to be adapted.
When initialize the ratectl state, a cap flag must be properly set and feed
to ieee80211_ratectl_init().  __findrate__ part should be repalced with our
ieee80211_ratectl_rates().

I've added  ieee80211_ratectl_rc_info_get() to be used to get the
ieee80211_rc_info. If found the tag, use it; if not, add a new one and use
it. Then we don't
need to free it explicitly (the tag is freed when associated mbuf is freed)
and this interface is unified to both __findrate__ and __complete__.

Thanks!

Chenchong


On Sat, Sep 14, 2013 at 11:21 PM, Adrian Chadd <adrian@freebsd.org> wrote:

> Ah, cool! I see you've only just made the other drivers compile; what's
> required to make them work? i guess a call
> to ieee80211_ratectl_rc_info_set() ?
>
> Maybe you should add a ieee80211_ratectl_rc_info_set_mbuf() helper that
> does the "lookup tag; if one exists use it else use a temporary one" code
> that you put in if_ath.c, if_ath_tx.c.
>
> Other than that, this is looking very good! thankyou!
>
>
> -adrian
>
>
>
> On 13 September 2013 20:52, Chenchong Qin <qinchenchong@gmail.com> wrote:
>
>> Hi,
>>
>> Here is latest update. Per-device ratectl statistics is implemented in
>> ath and attached when ath is attaching.
>>
>> Thanks!
>>
>> Chenchong
>>
>>
>> On Sat, Sep 14, 2013 at 3:37 AM, Adrian Chadd <adrian@freebsd.org> wrote:
>>
>>> Sweet, thanks!
>>>
>>>
>>>
>>> -adrian
>>>
>>>
>>>
>>> On 13 September 2013 09:11, Chenchong Qin <qinchenchong@gmail.com>wrote:
>>>
>>>> Hi!
>>>>
>>>> Here is some updates.
>>>>
>>>> Another member is added to ieee80211_rc_info to record value of the
>>>> maximum aggregate size. Then, in aggregation scenario, ratectl algo can
>>>> inform aggregation selection code of proper maximum aggregate size.
>>>>
>>>> Per-vap ratectl statistics is exported through sysctl. When
>>>> ieee80211_ratectl_init() is called, this statistics api is attached. It's
>>>> convenient to implement the per-device api -- just traverse the vap list
>>>> and call per-vap api for each vap. But, we know that ratectl of net80211
>>>> provides service to vap-granularity object, not to device directly. So, is
>>>> it more suitable to implement the per-device api in device driver (i.e.
>>>> attach per-device api when attaching the device)?
>>>>
>>>> Code will be posted later.
>>>>
>>>> Thanks!
>>>>
>>>> Chenchong
>>>>
>>>>
>>>> On Thu, Sep 12, 2013 at 2:05 AM, Adrian Chadd <adrian@freebsd.org>wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> For now, yes, you have to assume that you won't always get a response
>>>>> for a rate lookup. The buffer may be sent with NOACK set, it may be deleted
>>>>> during a channel change or scan, etc.
>>>>>
>>>>> And yes - the rate control lookup stuff for aggregate frames is a bit
>>>>> messy. It would be nice for the rate control code to return the rate _and_
>>>>> the maximum aggregate size, in case the aggregation selection wants to cap
>>>>> how long frames are at the given choice.
>>>>>
>>>>>
>>>>>
>>>>> -adrian
>>>>>
>>>>>
>>>>>
>>>>> On 11 September 2013 10:29, Chenchong Qin <qinchenchong@gmail.com>wrote:
>>>>>
>>>>>> Hi!
>>>>>>
>>>>>> I've added some aggregation support here!
>>>>>>
>>>>>> At first I intend to pass subframe informations(nframes, per-subframe
>>>>>> length etc.)
>>>>>> to the ratectl api. But it seems to be a paradox that rate lookup
>>>>>> must be performed
>>>>>> before the ampdu is formed (aggregation limit based on the rate
>>>>>> control decision
>>>>>> is need) and subframe informations can be obtain only after the ampdu
>>>>>> is formed.
>>>>>> So, I add a new ieee80211_rc_info flag to ieee80211_ratectl to let it
>>>>>> distinguish
>>>>>> aggregation and non-aggregation scenarios. If rate lookup is called
>>>>>> in an aggregation
>>>>>> scenario, this flag is set. Then, ratectl algo knows that it's now
>>>>>> finding rates for an
>>>>>> ampdu and the framelen which records len of the first frame can be
>>>>>> ignored. When
>>>>>> it comes to complete period, tx status that shows number of subframes
>>>>>> been sent
>>>>>> and number of subframes been successfully received is passed to the
>>>>>> ratectl api.
>>>>>>
>>>>>> I also get a question here - whether one tx that doesn't perform rate
>>>>>> lookup will call
>>>>>> the complete procedure?
>>>>>>
>>>>>> Thanks!
>>>>>>
>>>>>> Chenchong
>>>>>>
>>>>>>
>>>>>> On Sun, Sep 8, 2013 at 11:18 PM, Chenchong Qin <
>>>>>> qinchenchong@gmail.com> wrote:
>>>>>>
>>>>>>> Hi!
>>>>>>>
>>>>>>> I've added the common ratectl state as an mbuf tag!
>>>>>>>
>>>>>>> After days of frustration (compile errors, boot failed, kernel
>>>>>>> panics, suddenly kernel freezing...), it seems that ath now can use
>>>>>>> 11n-aware net80211 ratectl api to do rate control. Attachment[0] is the
>>>>>>> diff of modifications to dev/ath. Changes to net80211 is minor this time.
>>>>>>> Just add some debug msgs to it. Please reference my gsoc svn repo<https://svnweb.freebsd.org/socsvn/soc2013/ccqin/head/>;
>>>>>>> .
>>>>>>>
>>>>>>> It's worth mentioning that sometimes the kernel will "freezing" (it
>>>>>>> looks like all things stop working, screen is freezing, keyboard and mouse
>>>>>>> are not responding) after wireless stuff start working for a while. At
>>>>>>> first, I consider it caused by my modification to ath. But this strange
>>>>>>> thing can also happen in a head kernel (r255382). Attachment[1] is some
>>>>>>> useful messages just before it happens. By the way, I use a AR9227 device.
>>>>>>>
>>>>>>> And, I found that, for aggregation scenario, ath gathers tx
>>>>>>> information and update the ratectl states. So, what we can do to net80211
>>>>>>> to let it support aggregation?
>>>>>>>
>>>>>>> Thanks!
>>>>>>>
>>>>>>> Chenchong
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Sep 3, 2013 at 9:29 AM, Chenchong Qin <
>>>>>>> qinchenchong@gmail.com> wrote:
>>>>>>>
>>>>>>>> OK!
>>>>>>>>
>>>>>>>> Thanks! :-)
>>>>>>>>
>>>>>>>> Chenchong
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Sep 3, 2013 at 1:56 AM, Adrian Chadd <adrian@freebsd.org>wrote:
>>>>>>>>
>>>>>>>>> Hi!
>>>>>>>>>
>>>>>>>>> You can declare an mbuf tag and use that. Look at M_TXCB in
>>>>>>>>> net80211 and how mbuf tags are added.
>>>>>>>>>
>>>>>>>>> I've long thought about adding a net80211 mbuf tag to represent
>>>>>>>>> -all- of the tx related state - TX callback, rate control, rate completion,
>>>>>>>>> aggregation state, retry count, etc. That way all the drivers can use it.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> -adrian
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>

[-- Attachment #2 --]
Index: net80211/ieee80211_amrr.c
===================================================================
--- net80211/ieee80211_amrr.c	(revision 254826)
+++ 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,18 +58,19 @@
 	((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 *);
 static int	amrr_update(struct ieee80211_amrr *,
     			struct ieee80211_amrr_node *, struct ieee80211_node *);
 static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
+static void	amrr_rates(struct ieee80211_node *, struct ieee80211_rc_info *);
 static void	amrr_tx_complete(const struct ieee80211vap *,
-    			const struct ieee80211_node *, int, 
-			void *, void *);
-static void	amrr_tx_update(const struct ieee80211vap *vap,
+    			const struct ieee80211_node *, struct ieee80211_rc_info *);
+static void	amrr_tx_update(const struct ieee80211vap *,
 			const struct ieee80211_node *, void *, void *, void *);
+static void	amrr_stats(const struct ieee80211vap *);
 static void	amrr_sysctlattach(struct ieee80211vap *,
 			struct sysctl_ctx_list *, struct sysctl_oid *);
 
@@ -85,9 +86,11 @@
 	.ir_node_init	= amrr_node_init,
 	.ir_node_deinit	= amrr_node_deinit,
 	.ir_rate	= amrr_rate,
+	.ir_rates	= amrr_rates,
 	.ir_tx_complete	= amrr_tx_complete,
 	.ir_tx_update	= amrr_tx_update,
 	.ir_setinterval	= amrr_setinterval,
+	.ir_stats	= amrr_stats,
 };
 IEEE80211_RATECTL_MODULE(amrr, 1);
 IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
@@ -105,7 +108,7 @@
 }
 
 static void
-amrr_init(struct ieee80211vap *vap)
+amrr_init(struct ieee80211vap *vap, uint32_t capabilities)
 {
 	struct ieee80211_amrr *amrr;
 
@@ -117,6 +120,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 +136,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,29 +155,20 @@
 		}
 	} else
 		amn = ni->ni_rctls;
+
 	amn->amn_amrr = amrr;
 	amn->amn_success = 0;
 	amn->amn_recovery = 0;
 	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 +176,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 +185,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 */
@@ -206,9 +193,8 @@
 	amn->amn_ticks = ticks;
 
 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-	    "AMRR: nrates=%d, initial rate %d",
-	    rs->rs_nrates,
-	    rate);
+	    "%s: AMRR: nrates=%d, initial rate %d",
+		__func__, rs->rs_nrates, rate);
 }
 
 static void
@@ -226,17 +212,12 @@
 
 	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,
+	    "%s: AMRR: current rate %d, txcnt=%d, retrycnt=%d",
+	    __func__, rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 	    amn->amn_txcnt,
 	    amn->amn_retrycnt);
 
@@ -256,8 +237,8 @@
 			amn->amn_success = 0;
 			rix++;
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
-			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
+			    "%s: AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
+			    __func__, rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 			    amn->amn_txcnt, amn->amn_retrycnt);
 		} else {
 			amn->amn_recovery = 0;
@@ -277,8 +258,8 @@
 			}
 			rix--;
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
-			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
-			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
+			    "%s: AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
+			    __func__, rs->rs_rates[rix] & IEEE80211_RATE_VAL,
 			    amn->amn_txcnt, amn->amn_retrycnt);
 		}
 		amn->amn_recovery = 0;
@@ -304,13 +285,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 +293,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;
@@ -330,6 +305,76 @@
 	return rix;
 }
 
+static void
+amrr_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
+{
+#define	RATE(_ix)	(rs->rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+#define	MCS(_ix)	(rs->rs_rates[(_ix)] | IEEE80211_RATE_MCS)
+	struct ieee80211_rc_series *rc = rc_info->iri_rc;
+	const struct ieee80211_rateset *rs = NULL;
+	const struct ieee80211_rate_table *rt = NULL;
+	int rix, code;
+
+	rs = ieee80211_ratectl_get_rateset(ni);
+	rt = ieee80211_get_ratetable(ni->ni_ic->ic_curchan);
+	IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_RATECTL, 
+	    "%s: channel flags: 0x%08x\n", __func__, 
+		ni->ni_ic->ic_curchan->ic_flags); 
+	
+	rix = amrr_rate(ni, NULL, 0);
+
+	rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0;
+
+	if (rs->rs_nrates > 0) {
+		code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix);
+		rc[0].rix = rt->rateCodeToIndex[code];
+
+		if (IEEE80211_RATECTL_HASCAP_MRR(ni->ni_vap)) {
+			rc[0].tries = 1;
+			rc[1].tries = 1;
+			rc[2].tries = 1;
+			rc[3].tries = 1;
+			if (--rix >= 0) {
+				code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix);
+				rc[1].rix = rt->rateCodeToIndex[code];
+			} else {
+				rc[1].rix = rt->rateCodeToIndex[0];
+			}
+			if (--rix >= 0) {
+				code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix);
+				rc[2].rix = rt->rateCodeToIndex[code];
+			} else {
+				rc[2].rix = rt->rateCodeToIndex[0];
+			}
+			if (rix >= 0) {
+				/* NB: only do this if we didn't already do it above */
+				code = ieee80211_ratectl_node_is11n(ni)? MCS(0) : RATE(0);
+				rc[3].rix = rt->rateCodeToIndex[code];
+			} else {
+				rc[3].rix = rt->rateCodeToIndex[0];
+			}
+		} else {
+			rc[0].tries = IEEE80211_RATECTL_TXMAXTRY;
+
+			rc[1].tries = 0;
+			rc[2].tries = 0;
+			rc[3].tries = 0;
+			rc[1].rix = rt->rateCodeToIndex[0];
+			rc[2].rix = rt->rateCodeToIndex[0];
+			rc[3].rix = rt->rateCodeToIndex[0];
+		}
+	}
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+	    "%s: AMRR rate sets(rix, tries). rc[0]:(%d, %d), rc[1]:(%d, %d), " 
+		"rc[2]:(%d, %d), rc[3]:(%d, %d)", __func__, 
+		rc[0].rix, rc[0].tries,
+		rc[1].rix, rc[1].tries, 
+		rc[2].rix, rc[2].tries, 
+		rc[3].rix, rc[3].tries);
+#undef RATE
+#undef MCS
+}
+
 /*
  * Update statistics with tx complete status.  Ok is non-zero
  * if the packet is known to be ACK'd.  Retries has the number
@@ -337,16 +382,22 @@
  */
 static void
 amrr_tx_complete(const struct ieee80211vap *vap,
-    const struct ieee80211_node *ni, int ok,
-    void *arg1, void *arg2 __unused)
+    const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
 {
 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
-	int retries = *(int *)arg1;
 
-	amn->amn_txcnt++;
-	if (ok)
-		amn->amn_success++;
-	amn->amn_retrycnt += retries;
+	/* update per vap statistics */
+	ieee80211_ratectl_update_stat(vap, rc_info);
+
+	amn->amn_txcnt += rc_info->iri_txcnt;
+	amn->amn_success += (rc_info->iri_txcnt - rc_info->iri_failcnt);
+	amn->amn_retrycnt += rc_info->iri_retrycnt;
+
+	IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni,
+	    "%s: AMRR tx complete. txcnt=%d(%d) success=%d(%d) retrycnt=%d(%d)\n",
+	    __func__, amn->amn_txcnt, rc_info->iri_txcnt,
+		amn->amn_success, (rc_info->iri_txcnt - rc_info->iri_failcnt),
+		amn->amn_retrycnt, rc_info->iri_retrycnt);
 }
 
 /*
@@ -364,8 +415,21 @@
 	amn->amn_txcnt = txcnt;
 	amn->amn_success = success;
 	amn->amn_retrycnt = retrycnt;
+	IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL, ni,
+	    "%s: AMRR tx update. txcnt=%d success=%d retrycnt=%d\n",
+	    __func__, txcnt, success, retrycnt);
 }
 
+static void
+amrr_stats(const struct ieee80211vap *vap)
+{
+	struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+	printf("tx count: %d (ok count: %d, fail count: %d)\n"
+			"retry count: %d (short retry: %d, long retry: %d)\n",
+			irs->irs_txcnt, irs->irs_txcnt - irs->irs_failcnt, irs->irs_failcnt,
+			irs->irs_retrycnt, irs->irs_shortretry, irs->irs_longretry);
+}
+
 static int
 amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
 {
Index: net80211/ieee80211_ratectl.c
===================================================================
--- net80211/ieee80211_ratectl.c	(revision 254826)
+++ 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)
 {
@@ -65,12 +100,36 @@
 	ratectls[type] = NULL;
 }
 
+static int
+ratectl_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+	struct ieee80211vap *vap = arg1;
+	int error, v;
+
+	v = 0;
+	error = sysctl_handle_int(oidp, &v, 0, req);
+	if (error || !req->newptr)
+		return error;
+
+	ieee80211_ratectl_stats(vap);
+	return 0;
+}
+
 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);
+
+	SYSCTL_ADD_PROC(vap->iv_sysctl, SYSCTL_CHILDREN(vap->iv_oid), 
+			OID_AUTO, "ratectl_stats", CTLTYPE_INT | CTLFLAG_RW, 
+			vap, 0, ratectl_sysctl_stats, "I", 
+			"per-vap net80211 ratectl statistics");
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL,
+	    "%s: ratectl initialized. caps=0x%08x\n",
+	    __func__, capabilities);
 }
 
 void
@@ -90,3 +149,110 @@
 	}
 	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;
+	struct ieee80211_rc_series *rc = rc_info->iri_rc;
+	const struct ieee80211_rate_table * rt = NULL;
+	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 dual-stream, shortgi, 2040 if HT is set */
+
+		if (IS_HT_RATE(rate)) {
+			rc[i].flags |= IEEE80211_RATECTL_FLAG_HT;
+
+			if (ieee80211_ratectl_hascap_cw40(ni))
+				rc[i].flags |= IEEE80211_RATECTL_FLAG_CW40;
+
+			if (ieee80211_ratectl_hascap_shortgi(ni))
+				rc[i].flags |= IEEE80211_RATECTL_FLAG_SGI;
+			/*
+			 * If we have STBC TX enabled and the receiver
+			 * can receive (at least) 1 stream STBC, AND it's
+			 * MCS 0-7, AND we have at least two chains enabled,
+			 * enable STBC.
+			 */
+			if (ieee80211_ratectl_hascap_stbc(ni) &&
+				(rate & IEEE80211_RATE_VAL) < 8 &&
+				HT_RC_2_STREAMS(rate) == 1)
+				rc[i].flags |= IEEE80211_RATECTL_FLAG_STBC;
+			
+			/*
+			 * 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 4ms frame length based
+			 * on the MCS rate, SGI and channel width flags.
+			 */
+			if (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;
+
+			/*
+			 * Only enable short preamble for legacy rates
+			 */
+			if (rc_info->iri_flags & IEEE80211_RATECTL_INFO_SP)
+				rc[i].flags |= IEEE80211_RATECTL_FLAG_SP;
+		}
+
+		/*
+		 * 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);
+		
+	}
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+	    "%s: flags: rc[0]:0x%08x, rc[1]:0x%08x, "
+		"rc[2]:0x%08x, rc[3]:0x%08x\n", __func__, 
+		rc[0].flags, rc[1].flags, rc[2].flags, rc[3].flags);
+}
+
Index: net80211/ieee80211_ratectl.h
===================================================================
--- net80211/ieee80211_ratectl.h	(revision 254826)
+++ 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,28 +40,115 @@
 #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_TXMAXTRY	11
+
+#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 */
+#define	IEEE80211_RATECTL_FLAG_SP		0x80	/* short preamble */
+
+/* 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];
+	uint32_t iri_framelen;
+	uint16_t iri_flags;			/* See below */
+	uint16_t iri_maxaggrsize;	/* Maximum aggregate size */
+
+	/* TX info */
+	uint8_t iri_txcnt;			/* TX count */
+	uint8_t iri_failcnt;		/* TX retry-fail count */
+	uint8_t iri_okcnt;			/* TX ok with or without retry */
+	uint8_t iri_retrycnt;		/* TX retry count */
+	uint8_t iri_shortretry;
+	uint8_t iri_longretry;
+	uint8_t iri_finaltsi;
+	uint8_t iri_txrate;			/* HW tx rate */
+};
+
+/* ieee80211_rc_info flags */
+#define	IEEE80211_RATECTL_INFO_SP		0x01	/* short preamble */
+#define	IEEE80211_RATECTL_INFO_AGGR		0x02	/* aggregation scenario */
+
+#define NET80211_TAG_RATECTL   1   /* net80211 ratectl state */
+
+/*
+ * 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 *);
+	    			  const struct ieee80211_node *, struct ieee80211_rc_info *);
 	void	(*ir_tx_update)(const struct ieee80211vap *,
 	    			const struct ieee80211_node *,
 	    			void *, void *, void *);
 	void	(*ir_setinterval)(const struct ieee80211vap *, int);
+	void	(*ir_stats)(const struct ieee80211vap *);
 };
 
 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);
 
@@ -74,6 +164,8 @@
 	const struct ieee80211vap *vap = ni->ni_vap;
 
 	vap->iv_rate->ir_node_init(ni);
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL,
+			"%s: net80211 ratectl node inited.\n", __func__);
 }
 
 static void __inline
@@ -82,6 +174,8 @@
 	const struct ieee80211vap *vap = ni->ni_vap;
 
 	vap->iv_rate->ir_node_deinit(ni);
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL,
+			"%s: net80211 ratectl node deinited.\n", __func__);
 }
 
 static int __inline
@@ -93,10 +187,26 @@
 }
 
 static void __inline
+ieee80211_ratectl_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
+{
+	const struct ieee80211vap *vap = ni->ni_vap;
+
+	if (rc_info->iri_flags & IEEE80211_RATECTL_INFO_AGGR) 
+		rc_info->iri_framelen = 0;
+
+	IEEE80211_DPRINTF(vap, IEEE80211_MSG_RATECTL,
+			"%s: find rate sets for %saggregation scenario.\n", __func__, 
+			(rc_info->iri_flags & IEEE80211_RATECTL_INFO_AGGR)? "" : "non-");
+
+	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)
+    const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
 {
-	vap->iv_rate->ir_tx_complete(vap, ni, status, arg1, arg2);
+	vap->iv_rate->ir_tx_complete(vap, ni, rc_info);
 }
 
 static void __inline
@@ -115,3 +225,135 @@
 		return;
 	vap->iv_rate->ir_setinterval(vap, msecs);
 }
+
+static void __inline
+ieee80211_ratectl_stats(const struct ieee80211vap *vap)
+{
+	printf("\n[%s]: net80211 ratectl statistics (%s)\n", 
+			vap->iv_ifp->if_xname, vap->iv_rate->ir_name);
+	if (vap->iv_rate->ir_stats == NULL)
+		return;
+	vap->iv_rate->ir_stats(vap);
+}
+
+static int __inline
+ieee80211_ratectl_hascap_cw40(const struct ieee80211_node *ni)
+{
+	return ni->ni_chw == 40;
+}
+
+static int __inline
+ieee80211_ratectl_hascap_shortgi(const struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	if (ni->ni_chw == 40 &&
+		ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40 &&
+		ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40)
+		return IEEE80211_RATECTL_TRUE;
+
+	if (ni->ni_chw == 20 &&
+		ic->ic_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 ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	if (ic->ic_htcaps & IEEE80211_HTCAP_TXSTBC &&
+		ni->ni_htcap & IEEE80211_HTCAP_RXSTBC_1STREAM &&
+		IEEE80211_RATECTL_HASCAP_MULTXCHAIN(ni->ni_vap))
+		return IEEE80211_RATECTL_TRUE;
+	
+	return IEEE80211_RATECTL_FALSE;
+}
+
+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)
+{
+	const struct ieee80211_rateset *rs = NULL;
+	/* 11n or not? Pick the right rateset */
+	if (ieee80211_ratectl_node_is11n(ni)) {
+		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;
+	}
+	return rs;
+}
+
+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;
+}
+
+static void __inline
+ieee80211_ratectl_rc_info_set(struct ieee80211_rc_info *rc_info,
+		uint8_t txcnt, uint8_t failcnt, uint32_t framelen,
+		uint8_t shortretry, uint8_t longretry,
+		uint8_t finaltsi, uint8_t txrate)
+{
+	rc_info->iri_txcnt = txcnt;
+	rc_info->iri_failcnt = failcnt;
+	rc_info->iri_okcnt = txcnt - failcnt;
+	rc_info->iri_framelen = framelen;
+	rc_info->iri_shortretry = shortretry;
+	rc_info->iri_longretry = longretry;
+	rc_info->iri_retrycnt = shortretry + longretry;
+	rc_info->iri_finaltsi = finaltsi;
+	rc_info->iri_txrate = txrate;
+}
+
+__inline static struct ieee80211_rc_info *
+ieee80211_ratectl_rc_info_get(struct ieee80211_node *ni,
+		struct mbuf *m)
+{
+	struct m_tag *mtag;
+
+    mtag = m_tag_locate(m, MTAG_ABI_NET80211, 
+			NET80211_TAG_RATECTL, NULL);
+
+	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+			"%s: %sratectl mbuf tag found.\n", __func__, 
+			(NULL == mtag? "no ":""));
+again:
+	if (NULL == mtag) {
+		mtag = m_tag_alloc(MTAG_ABI_NET80211, NET80211_TAG_RATECTL,
+				sizeof(struct ieee80211_rc_info), M_NOWAIT);
+		if (NULL == mtag) {
+			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
+					"%s: can't alloc mbuf tag for ratectl.\n", __func__);
+			goto again;
+		}
+		bzero(mtag + 1, mtag->m_tag_len);
+		m_tag_prepend(m, mtag);
+	}
+	return (struct ieee80211_rc_info*)(mtag + 1);
+}
+
+#endif
Index: net80211/ieee80211_freebsd.c
===================================================================
--- net80211/ieee80211_freebsd.c	(revision 254826)
+++ net80211/ieee80211_freebsd.c	(working copy)
@@ -262,7 +262,9 @@
 		"driver_caps", CTLFLAG_RW, &vap->iv_caps, 0,
 		"driver capabilities");
 #ifdef IEEE80211_DEBUG
+	ieee80211_debug |= (IEEE80211_MSG_RATECTL|IEEE80211_MSG_NODE|IEEE80211_MSG_SCAN);
 	vap->iv_debug = ieee80211_debug;
+	if_printf(ifp, "%s: iv_debug=0x%08x\n", __func__, vap->iv_debug);
 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
 		"debug", CTLFLAG_RW, &vap->iv_debug, 0,
 		"control debugging printfs");
Index: net80211/ieee80211_ratectl_none.c
===================================================================
--- net80211/ieee80211_ratectl_none.c	(revision 254826)
+++ 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;
 }
 
@@ -78,8 +113,7 @@
 
 static void
 none_tx_complete(const struct ieee80211vap *vap,
-    const struct ieee80211_node *ni, int ok,
-    void *arg1, void *arg2 __unused)
+    const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
 {
 }
 
Index: net80211/ieee80211_rssadapt.c
===================================================================
--- net80211/ieee80211_rssadapt.c	(revision 254826)
+++ net80211/ieee80211_rssadapt.c	(working copy)
@@ -74,7 +74,7 @@
 				    parm##_denom)
 
 static void	rssadapt_setinterval(const struct ieee80211vap *, int);
-static void	rssadapt_init(struct ieee80211vap *);
+static void	rssadapt_init(struct ieee80211vap *, uint32_t);
 static void	rssadapt_deinit(struct ieee80211vap *);
 static void	rssadapt_updatestats(struct ieee80211_rssadapt_node *);
 static void	rssadapt_node_init(struct ieee80211_node *);
@@ -84,8 +84,7 @@
 static void	rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
 			int, int);
 static void	rssadapt_tx_complete(const struct ieee80211vap *,
-    			const struct ieee80211_node *, int,
-			void *, void *);
+    			const struct ieee80211_node *, struct ieee80211_rc_info *);
 static void	rssadapt_sysctlattach(struct ieee80211vap *,
 			struct sysctl_ctx_list *, struct sysctl_oid *);
 
@@ -121,7 +120,7 @@
 }
 
 static void
-rssadapt_init(struct ieee80211vap *vap)
+rssadapt_init(struct ieee80211vap *vap, uint32_t capabilities)
 {
 	struct ieee80211_rssadapt *rs;
 
@@ -134,6 +133,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;
+
 	rs->vap = vap;
 	rssadapt_setinterval(vap, 500 /* msecs */);
 	rssadapt_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
@@ -307,10 +310,11 @@
 
 static void
 rssadapt_tx_complete(const struct ieee80211vap *vap,
-    const struct ieee80211_node *ni, int success, void *arg1, void *arg2)
+    const struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
 {
 	struct ieee80211_rssadapt_node *ra = ni->ni_rctls;
-	int pktlen = *(int *)arg1, rssi = *(int *)arg2;
+	/*int pktlen = *(int *)arg1, rssi = *(int *)arg2;*/
+	int pktlen = 0, rssi = 0, success = 0; /* just make it compiled. qcc */
 
 	if (success) {
 		ra->ra_nok++;
Index: net80211/ieee80211_rc_sample.c
===================================================================
--- net80211/ieee80211_rc_sample.c	(revision 0)
+++ net80211/ieee80211_rc_sample.c	(revision 256825)
@@ -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: net80211/ieee80211_rc_sample.h
===================================================================
--- net80211/ieee80211_rc_sample.h	(revision 0)
+++ net80211/ieee80211_rc_sample.h	(revision 256825)
@@ -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: net80211/ieee80211_ratectl_none.h
===================================================================
--- net80211/ieee80211_ratectl_none.h	(revision 0)
+++ net80211/ieee80211_ratectl_none.h	(revision 256825)
@@ -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: net80211/ieee80211_rc_sample_txsched.h
===================================================================
--- net80211/ieee80211_rc_sample_txsched.h	(revision 0)
+++ net80211/ieee80211_rc_sample_txsched.h	(revision 256825)
@@ -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_ */
Index: net80211/ieee80211_rssadapt.h
===================================================================
--- net80211/ieee80211_rssadapt.h	(revision 254826)
+++ net80211/ieee80211_rssadapt.h	(working copy)
@@ -32,6 +32,8 @@
 #ifndef _NET80211_IEEE80211_RSSADAPT_H_
 #define _NET80211_IEEE80211_RSSADAPT_H_
 
+#include <net80211/ieee80211_ratectl.h>
+
 /* Data-rate adaptation loosely based on "Link Adaptation Strategy
  * for IEEE 802.11 WLAN via Received Signal Strength Measurement"
  * by Javier del Prado Pavon and Sunghyun Choi.
@@ -43,6 +45,7 @@
 #define	IEEE80211_RSSADAPT_BKTPOWER	3	/* 2**_BKTPOWER */
 
 struct ieee80211_rssadapt {
+	struct ieee80211_rc_stat rssadapt_stat;
 	const struct ieee80211vap *vap;
 	int	interval;			/* update interval (ticks) */
 };
Index: net80211/ieee80211_amrr.h
===================================================================
--- net80211/ieee80211_amrr.h	(revision 254826)
+++ 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: dev/iwn/if_iwn.c
===================================================================
--- dev/iwn/if_iwn.c	(revision 254826)
+++ 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;
@@ -2499,7 +2500,8 @@
 	uint64_t bitmap;
 	uint16_t ssn;
 	uint8_t tid;
-	int ackfailcnt = 0, i, lastidx, qid, *res, shift;
+	/* int failcnt = 0; */
+	int i, lastidx, qid, *res, shift;
 
 	bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD);
 
@@ -2563,12 +2565,18 @@
 	for (i = 0; bitmap; i++) {
 		if ((bitmap & 1) == 0) {
 			ifp->if_oerrors++;
+#if 0
 			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
 			    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
+#endif
+		ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */	
 		} else {
 			ifp->if_opackets++;
+#if 0
 			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
 			    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
+#endif
+			ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */
 		}
 		bitmap >>= 1;
 	}
@@ -2807,12 +2815,18 @@
 	 */
 	if (status & IWN_TX_FAIL) {
 		ifp->if_oerrors++;
+#if 0
 		ieee80211_ratectl_tx_complete(vap, ni,
 		    IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL);
+#endif
+		ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 	} else {
 		ifp->if_opackets++;
+#if 0
 		ieee80211_ratectl_tx_complete(vap, ni,
 		    IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL);
+#endif
+		ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 	}
 	m_freem(m);
 	ieee80211_free_node(ni);
Index: dev/bwi/if_bwi.c
===================================================================
--- dev/bwi/if_bwi.c	(revision 254826)
+++ 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);
@@ -3377,9 +3378,12 @@
 			 * well so to avoid over-aggressive downshifting we
 			 * treat any number of retries as "1".
 			 */
+#if 0
 			ieee80211_ratectl_tx_complete(vap, ni,
 			    (data_txcnt > 1) ? IEEE80211_RATECTL_TX_SUCCESS :
 			        IEEE80211_RATECTL_TX_FAILURE, &acked, NULL);
+#endif
+			ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 		}
 
 		/*
Index: dev/bwn/if_bwn.c
===================================================================
--- dev/bwn/if_bwn.c	(revision 254826)
+++ 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,
@@ -8991,8 +8992,9 @@
 	struct bwn_stats *stats = &mac->mac_stats;
 	struct ieee80211_node *ni;
 	struct ieee80211vap *vap;
-	int retrycnt = 0, slot;
-
+	/*int retrycnt = 0, slot;*/
+	int slot;
+	
 	BWN_ASSERT_LOCKED(mac->mac_sc);
 
 	if (status->im)
@@ -9020,11 +9022,14 @@
 				if (meta->mt_islast) {
 					ni = meta->mt_ni;
 					vap = ni->ni_vap;
+#if 0
 					ieee80211_ratectl_tx_complete(vap, ni,
 					    status->ack ?
 					      IEEE80211_RATECTL_TX_SUCCESS :
 					      IEEE80211_RATECTL_TX_FAILURE,
 					    &retrycnt, 0);
+#endif
+					ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 					break;
 				}
 				slot = bwn_dma_nextslot(dr, slot);
@@ -9041,11 +9046,14 @@
 			}
 			ni = tp->tp_ni;
 			vap = ni->ni_vap;
+#if 0
 			ieee80211_ratectl_tx_complete(vap, ni,
 			    status->ack ?
 			      IEEE80211_RATECTL_TX_SUCCESS :
 			      IEEE80211_RATECTL_TX_FAILURE,
 			    &retrycnt, 0);
+#endif
+			ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 		}
 		bwn_pio_handle_txeof(mac, status);
 	}
Index: dev/usb/wlan/if_zyd.c
===================================================================
--- dev/usb/wlan/if_zyd.c	(revision 254826)
+++ 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 */
@@ -664,12 +665,15 @@
 			 */
 			ni = ieee80211_find_txnode(vap, retry->macaddr);
 			if (ni != NULL) {
-				int retrycnt =
-				    (int)(le16toh(retry->count) & 0xff);
+				/*int retrycnt =
+				    (int)(le16toh(retry->count) & 0xff);*/
 				
+#if 0
 				ieee80211_ratectl_tx_complete(vap, ni,
 				    IEEE80211_RATECTL_TX_FAILURE,
 				    &retrycnt, NULL);
+#endif
+				ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 				ieee80211_free_node(ni);
 			}
 			if (le16toh(retry->count) & 0x100)
Index: dev/usb/wlan/if_rum.c
===================================================================
--- dev/usb/wlan/if_rum.c	(revision 254826)
+++ 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: dev/usb/wlan/if_ural.c
===================================================================
--- dev/usb/wlan/if_ural.c	(revision 254826)
+++ 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: dev/usb/wlan/if_run.c
===================================================================
--- dev/usb/wlan/if_run.c	(revision 254826)
+++ 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: dev/wpi/if_wpi.c
===================================================================
--- dev/wpi/if_wpi.c	(revision 254826)
+++ 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;
@@ -1596,8 +1597,11 @@
 		DPRINTFN(WPI_DEBUG_TX, ("%d retries\n", stat->ntries));
 		retrycnt = 1;
 	}
+#if 0
 	ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS,
 	    &retrycnt, NULL);
+#endif
+	ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
 
 	/* XXX oerrors should only count errors !maxtries */
 	if ((le32toh(stat->status) & 0xff) != 1)
Index: dev/ral/rt2560.c
===================================================================
--- dev/ral/rt2560.c	(revision 254826)
+++ 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)
@@ -955,9 +956,13 @@
 
 			DPRINTFN(sc, 10, "%s\n", "data frame sent successfully");
 			if (data->rix != IEEE80211_FIXED_RATE_NONE)
+				ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 				ieee80211_ratectl_tx_complete(vap, ni,
 				    IEEE80211_RATECTL_TX_SUCCESS,
 				    &retrycnt, NULL);
+#endif
+
 			ifp->if_opackets++;
 			break;
 
@@ -967,9 +972,12 @@
 			DPRINTFN(sc, 9, "data frame sent after %u retries\n",
 			    retrycnt);
 			if (data->rix != IEEE80211_FIXED_RATE_NONE)
+				ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 				ieee80211_ratectl_tx_complete(vap, ni,
 				    IEEE80211_RATECTL_TX_SUCCESS,
 				    &retrycnt, NULL);
+#endif
 			ifp->if_opackets++;
 			break;
 
@@ -979,9 +987,12 @@
 			DPRINTFN(sc, 9, "data frame failed after %d retries\n",
 			    retrycnt);
 			if (data->rix != IEEE80211_FIXED_RATE_NONE)
+				ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 				ieee80211_ratectl_tx_complete(vap, ni,
 				    IEEE80211_RATECTL_TX_FAILURE,
 				    &retrycnt, NULL);
+#endif
 			ifp->if_oerrors++;
 			break;
 
Index: dev/ral/rt2661.c
===================================================================
--- dev/ral/rt2661.c	(revision 254826)
+++ 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)
@@ -905,9 +906,12 @@
 			DPRINTFN(sc, 10, "data frame sent successfully after "
 			    "%d retries\n", retrycnt);
 			if (data->rix != IEEE80211_FIXED_RATE_NONE)
+				ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 				ieee80211_ratectl_tx_complete(vap, ni,
 				    IEEE80211_RATECTL_TX_SUCCESS,
 				    &retrycnt, NULL);
+#endif
 			ifp->if_opackets++;
 			break;
 
@@ -917,9 +921,12 @@
 			DPRINTFN(sc, 9, "%s\n",
 			    "sending data frame failed (too much retries)");
 			if (data->rix != IEEE80211_FIXED_RATE_NONE)
+				ieee80211_ratectl_tx_complete(vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 				ieee80211_ratectl_tx_complete(vap, ni,
 				    IEEE80211_RATECTL_TX_FAILURE,
 				    &retrycnt, NULL);
+#endif
 			ifp->if_oerrors++;
 			break;
 
Index: dev/ral/rt2860.c
===================================================================
--- dev/ral/rt2860.c	(revision 254826)
+++ 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)
@@ -1121,11 +1122,17 @@
 				retrycnt = 1;
 			else
 				retrycnt = 0;
+			ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
 			    IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL);
+#endif
 		} else {
+			ieee80211_ratectl_tx_complete(ni->ni_vap, ni, NULL); /* just make it compiled. qcc */
+#if 0
 			ieee80211_ratectl_tx_complete(ni->ni_vap, ni,
 			    IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL);
+#endif
 			ifp->if_oerrors++;
 		}
 	}
Index: dev/ath/if_ath_sysctl.c
===================================================================
--- dev/ath/if_ath_sysctl.c	(revision 254826)
+++ dev/ath/if_ath_sysctl.c	(working copy)
@@ -71,6 +71,7 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_ratectl.h>
 #ifdef IEEE80211_SUPPORT_SUPERG
 #include <net80211/ieee80211_superg.h>
 #endif
@@ -602,6 +603,28 @@
 }
 #endif /* ATH_DEBUG_ALQ */
 
+static int
+ath_sysctl_ratectl_stats(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	struct ieee80211com *ic = sc->sc_ifp->if_l2com; 
+	struct ieee80211vap *vap;
+	int error, v;
+
+	v = 0;
+	error = sysctl_handle_int(oidp, &v, 0, req);
+	if (error || !req->newptr)
+		return error;
+
+	printf("\n[%s]: per-device net80211 ratectl statistics\n", 
+			ic->ic_ifp->if_xname);
+	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
+		ieee80211_ratectl_stats(vap);
+	}
+		
+	return 0;
+}
+
 void
 ath_sysctlattach(struct ath_softc *sc)
 {
@@ -792,6 +815,10 @@
 #ifdef	ATH_DEBUG_ALQ
 	ath_sysctl_alq_attach(sc);
 #endif
+	
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ratectl_stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_ratectl_stats, "I", "per-device net80211 ratectl statistics");
 }
 
 static int
Index: dev/ath/if_ath_tx_ht.c
===================================================================
--- dev/ath/if_ath_tx_ht.c	(revision 254826)
+++ dev/ath/if_ath_tx_ht.c	(working copy)
@@ -64,6 +64,8 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_regdomain.h>
+
+#include <net80211/ieee80211_ratectl.h>
 #ifdef IEEE80211_SUPPORT_SUPERG
 #include <net80211/ieee80211_superg.h>
 #endif
@@ -221,13 +223,47 @@
 void
 ath_tx_rate_fill_rcflags(struct ath_softc *sc, struct ath_buf *bf)
 {
-	struct ieee80211_node *ni = bf->bf_node;
-	struct ieee80211com *ic = ni->ni_ic;
+	// struct ieee80211_node *ni = bf->bf_node;
+	// struct ieee80211com *ic = ni->ni_ic;
 	const HAL_RATE_TABLE *rt = sc->sc_currates;
-	struct ath_rc_series *rc = bf->bf_state.bfs_rc;
+	struct ath_rc_series *ath_rc = bf->bf_state.bfs_rc;
+	struct ieee80211_rc_info *rc_info = NULL;
+	struct ieee80211_rc_series *rc = NULL;
+	struct m_tag *mtag;
 	uint8_t rate;
 	int i;
 
+	mtag = m_tag_locate(bf->bf_m, MTAG_ABI_NET80211, 
+			NET80211_TAG_RATECTL, NULL);
+	if (NULL == mtag)
+		return;
+
+	rc_info = (struct ieee80211_rc_info*)(mtag + 1);
+	rc = rc_info->iri_rc;
+
+	for (i = 0; i < IEEE80211_RATECTL_NUM; i++) {
+		if (rc[i].tries == 0)
+			continue;
+
+		rate = rt->info[rc[i].rix].rateCode;
+		if (rc[i].flags & IEEE80211_RATECTL_FLAG_SP)
+			rate |= rt->info[rc[i].rix].shortPreamble;
+
+		rc[i].ratecode = rate;
+
+		ath_rc[i].rix = rc[i].rix;
+		ath_rc[i].tries = rc[i].tries;
+		ath_rc[i].flags = rc[i].flags;
+		ath_rc[i].ratecode = rc[i].ratecode;
+		ath_rc[i].tx_power_cap = rc[i].tx_power_cap;
+		ath_rc[i].max4msframelen = rc[i].max4msframelen;
+		
+		DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
+		    "%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n",
+		    __func__, i, rate, rc[i].flags, rc[i].max4msframelen);
+	}
+
+#if 0
 	for (i = 0; i < ATH_RC_NUM; i++) {
 		rc[i].flags = 0;
 		if (rc[i].tries == 0)
@@ -325,6 +361,7 @@
 		    "%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n",
 		    __func__, i, rate, rc[i].flags, rc[i].max4msframelen);
 	}
+#endif
 }
 
 /*
Index: dev/ath/if_ath_tx.c
===================================================================
--- dev/ath/if_ath_tx.c	(revision 254826)
+++ dev/ath/if_ath_tx.c	(working copy)
@@ -72,6 +72,8 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_regdomain.h>
+
+#include <net80211/ieee80211_ratectl.h>
 #ifdef IEEE80211_SUPPORT_SUPERG
 #include <net80211/ieee80211_superg.h>
 #endif
@@ -1387,6 +1389,9 @@
 static void
 ath_tx_do_ratelookup(struct ath_softc *sc, struct ath_buf *bf)
 {
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ieee80211_rc_info *rc_info = NULL;
+	struct ieee80211_rc_series *rc = NULL;
 	uint8_t rate, rix;
 	int try0;
 
@@ -1395,7 +1400,7 @@
 
 	/* Get rid of any previous state */
 	bzero(bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc));
-
+#if 0
 	ATH_NODE_LOCK(ATH_NODE(bf->bf_node));
 	ath_rate_findrate(sc, ATH_NODE(bf->bf_node), bf->bf_state.bfs_shpream,
 	    bf->bf_state.bfs_pktlen, &rix, &try0, &rate);
@@ -1409,7 +1414,25 @@
 		ath_rate_getxtxrates(sc, ATH_NODE(bf->bf_node), rix,
 		    bf->bf_state.bfs_rc);
 	ATH_NODE_UNLOCK(ATH_NODE(bf->bf_node));
+#endif
 
+	/* net80211 ratectl */
+	rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m);
+	rc = rc_info->iri_rc;
+	
+	rc_info->iri_flags = 0;
+	if (bf->bf_state.bfs_shpream)
+		rc_info->iri_flags |= IEEE80211_RATECTL_INFO_SP;
+	if (bf->bf_state.bfs_aggr)
+		rc_info->iri_flags |= IEEE80211_RATECTL_INFO_AGGR;
+	rc_info->iri_framelen = bf->bf_state.bfs_pktlen;
+
+	ieee80211_ratectl_rates(ni, rc_info);
+
+	rix = rc[0].rix;
+	try0 = rc[0].tries;
+	rate = ni->ni_txrate;
+
 	sc->sc_txrix = rix;	/* for LED blinking */
 	sc->sc_lastdatarix = rix;	/* for fast frames */
 	bf->bf_state.bfs_try0 = try0;
@@ -1530,7 +1553,8 @@
 	 * frames that must go out - eg management/raw frames.
 	 */
 	bf->bf_state.bfs_txflags |= HAL_TXDESC_CLRDMASK;
-
+	
+	bf->bf_state.bfs_aggr = 0;
 	/* Setup the descriptor before handoff */
 	ath_tx_do_ratelookup(sc, bf);
 	ath_tx_calc_duration(sc, bf);
@@ -4058,6 +4082,7 @@
 	int tid = bf->bf_state.bfs_tid;
 	struct ath_tid *atid = &an->an_tid[tid];
 	struct ath_tx_status *ts = &bf->bf_status.ds_txstat;
+	struct ieee80211_rc_info *rc_info = NULL;
 
 	/* The TID state is protected behind the TXQ lock */
 	ATH_TX_LOCK(sc);
@@ -4108,9 +4133,23 @@
 	 * during a hw queue drain and the frame wanted an ACK.
 	 */
 	if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0))
+	{
+#if 0
 		ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc,
 		    ts, bf->bf_state.bfs_pktlen,
 		    1, (ts->ts_status == 0) ? 0 : 1);
+#endif
+	
+		/* net80211 ratectl */
+		rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m);
+		ieee80211_ratectl_rc_info_set(rc_info,
+				1, (ts->ts_status == 0 ? 0 : 1), 
+				bf->bf_state.bfs_pktlen,
+				ts->ts_shortretry, ts->ts_longretry,
+				ts->ts_finaltsi, ts->ts_rate);
+		rc_info->iri_flags &= ~IEEE80211_RATECTL_INFO_AGGR;	
+		ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
+	}
 
 	ath_tx_default_comp(sc, bf, fail);
 }
@@ -4475,6 +4514,8 @@
 	int drops = 0;
 	struct ieee80211_tx_ampdu *tap;
 	ath_bufhead bf_cq;
+	struct ath_tx_status ts = bf_first->bf_status.ds_txstat;
+	struct ieee80211_rc_info *rc_info = NULL;
 
 	TAILQ_INIT(&bf_q);
 	TAILQ_INIT(&bf_cq);
@@ -4485,10 +4526,23 @@
 	 * XXX use the length in the first frame in the series;
 	 * XXX just so things are consistent for now.
 	 */
+#if 0
 	ath_tx_update_ratectrl(sc, ni, bf_first->bf_state.bfs_rc,
 	    &bf_first->bf_status.ds_txstat,
 	    bf_first->bf_state.bfs_pktlen,
 	    bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes);
+#endif
+	
+	/* net80211 ratectl */
+	rc_info = ieee80211_ratectl_rc_info_get(ni, bf_first->bf_m);
+	ieee80211_ratectl_rc_info_set(rc_info,
+			bf_first->bf_state.bfs_nframes, 
+			bf_first->bf_state.bfs_nframes,
+			bf_first->bf_state.bfs_pktlen,
+			ts.ts_shortretry, ts.ts_longretry,
+			ts.ts_finaltsi, ts.ts_rate);
+	rc_info->iri_flags |= IEEE80211_RATECTL_INFO_AGGR;
+	ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
 
 	ATH_TX_LOCK(sc);
 	tap = ath_tx_get_tx_tid(an, tid->tid);
@@ -4626,9 +4680,12 @@
 	int nframes = 0, nbad = 0, nf;
 	int pktlen;
 	/* XXX there's too much on the stack? */
+#if 0
 	struct ath_rc_series rc[ATH_RC_NUM];
+#endif
 	int txseq;
-
+	struct ieee80211_rc_info *rc_info = NULL;
+	struct mbuf *m;
 	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR, "%s: called; hwq_depth=%d\n",
 	    __func__, atid->hwq_depth);
 
@@ -4748,7 +4805,7 @@
 	isaggr = bf_first->bf_state.bfs_aggr;
 	ba[0] = ts.ts_ba_low;
 	ba[1] = ts.ts_ba_high;
-
+#if 0
 	/*
 	 * Copy the TX completion status and the rate control
 	 * series from the first descriptor, as it may be freed
@@ -4756,6 +4813,11 @@
 	 * into things.
 	 */
 	memcpy(rc, bf_first->bf_state.bfs_rc, sizeof(rc));
+#endif
+	/* 
+	 * Get the mbuf here, as bf_first will be set to NULL later. 
+	 */
+	m = bf_first->bf_m; 
 
 	DPRINTF(sc, ATH_DEBUG_SW_TX_AGGR,
 	    "%s: txa_start=%d, tx_ok=%d, status=%.8x, flags=%.8x, "
@@ -4873,8 +4935,20 @@
 	 * control code.
 	 */
 	if (fail == 0)
+	{
+#if 0	
 		ath_tx_update_ratectrl(sc, ni, rc, &ts, pktlen, nframes,
 		    nbad);
+#endif
+		/* net80211 ratectl */
+		rc_info = ieee80211_ratectl_rc_info_get(ni, m);
+		ieee80211_ratectl_rc_info_set(rc_info,
+					nframes, nbad, pktlen,
+					ts.ts_shortretry, ts.ts_longretry,
+					ts.ts_finaltsi, ts.ts_rate);
+		rc_info->iri_flags |= IEEE80211_RATECTL_INFO_AGGR;
+		ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
+	}
 
 	/*
 	 * send bar if we dropped any frames
@@ -4948,6 +5022,7 @@
 	struct ath_tid *atid = &an->an_tid[tid];
 	struct ath_tx_status ts;
 	int drops = 0;
+	struct ieee80211_rc_info *rc_info = NULL;
 
 	/*
 	 * Take a copy of this; filtering/cloning the frame may free the
@@ -4962,11 +5037,22 @@
 	 * Do it outside of the TXQ lock.
 	 */
 	if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 0))
+	{
+#if 0	
 		ath_tx_update_ratectrl(sc, ni, bf->bf_state.bfs_rc,
 		    &bf->bf_status.ds_txstat,
 		    bf->bf_state.bfs_pktlen,
 		    1, (ts.ts_status == 0) ? 0 : 1);
-
+#endif
+		rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m);
+		ieee80211_ratectl_rc_info_set(rc_info,
+				1, (ts.ts_status == 0 ? 0 : 1), 
+				bf->bf_state.bfs_pktlen,
+				ts.ts_shortretry, ts.ts_longretry,
+				ts.ts_finaltsi, ts.ts_rate);
+		rc_info->iri_flags &= ~IEEE80211_RATECTL_INFO_AGGR;
+		ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
+	}
 	/*
 	 * This is called early so atid->hwq_depth can be tracked.
 	 * This unfortunately means that it's released and regrabbed
@@ -5183,6 +5269,15 @@
 		}
 
 		/*
+		 * If non-aggregate scenario appears, this will be 
+		 * turned off.
+		 *
+		 * This flag is used by ath_tx_do_ratelookup() to 
+		 * distinguish aggr/non-aggr scenario.  
+		 */
+		bf->bf_state.bfs_aggr = 1;
+
+		/*
 		 * If the packet doesn't fall within the BAW (eg a NULL
 		 * data frame), schedule it directly; continue.
 		 */
@@ -5425,6 +5520,7 @@
 		/* Update CLRDMASK just before this frame is queued */
 		ath_tx_update_clrdmask(sc, tid, bf);
 
+		bf->bf_state.bfs_aggr = 0; 
 		/* Program descriptors + rate control */
 		ath_tx_do_ratelookup(sc, bf);
 		ath_tx_calc_duration(sc, bf);
Index: dev/ath/if_ath.c
===================================================================
--- dev/ath/if_ath.c	(revision 254826)
+++ dev/ath/if_ath.c	(working copy)
@@ -82,6 +82,8 @@
 
 #include <net80211/ieee80211_var.h>
 #include <net80211/ieee80211_regdomain.h>
+
+#include <net80211/ieee80211_ratectl.h>
 #ifdef IEEE80211_SUPPORT_SUPERG
 #include <net80211/ieee80211_superg.h>
 #endif
@@ -322,6 +324,7 @@
 	sc->sc_ah = ah;
 	sc->sc_invalid = 0;	/* ready to go, enable interrupt handling */
 #ifdef	ATH_DEBUG
+	ath_debug |= ATH_DEBUG_NODE; 
 	sc->sc_debug = ath_debug;
 #endif
 
@@ -1380,6 +1383,16 @@
 	}
 	ATH_UNLOCK(sc);
 
+	uint32_t caps = 0;
+	if (sc->sc_mrretry)
+		caps |= IEEE80211_RATECTL_CAP_MRR;
+	if (sc->sc_mrrprot)
+		caps |= IEEE80211_RATECTL_CAP_MRRPROT;
+	if (sc->sc_txchainmask > 1)
+		caps |= IEEE80211_RATECTL_CAP_MULTXCHAIN;
+
+	ieee80211_ratectl_init(vap, caps);
+	
 	/* complete setup */
 	ieee80211_vap_attach(vap, ath_media_change, ieee80211_media_status);
 	return vap;
@@ -1414,6 +1427,7 @@
 		ath_stoprecv(sc, 1);		/* stop recv side */
 	}
 
+	ieee80211_ratectl_deinit(vap);
 	ieee80211_vap_detach(vap);
 
 	/*
@@ -4042,6 +4056,7 @@
 {
 	struct ieee80211_node *ni = bf->bf_node;
 	struct ath_node *an = NULL;
+	struct ieee80211_rc_info *rc_info = NULL;
 
 	ATH_TX_UNLOCK_ASSERT(sc);
 	ATH_TXQ_UNLOCK_ASSERT(txq);
@@ -4068,10 +4083,21 @@
 			 * XXX assume this isn't an aggregate
 			 * frame.
 			 */
+#if 0
 			ath_tx_update_ratectrl(sc, ni,
 			     bf->bf_state.bfs_rc, ts,
 			    bf->bf_state.bfs_pktlen, 1,
 			    (ts->ts_status == 0 ? 0 : 1));
+#endif
+			/* net80211 ratectl */
+			rc_info = ieee80211_ratectl_rc_info_get(ni, bf->bf_m);
+			ieee80211_ratectl_rc_info_set(rc_info,
+					1, (ts->ts_status == 0 ? 0 : 1), 
+					bf->bf_state.bfs_pktlen,
+					ts->ts_shortretry, ts->ts_longretry,
+					ts->ts_finaltsi, ts->ts_rate);
+			rc_info->iri_flags &= ~IEEE80211_RATECTL_INFO_AGGR;	
+			ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
 		}
 		ath_tx_default_comp(sc, bf, 0);
 	} else
@@ -5574,9 +5600,15 @@
 
 	an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate);
 	an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate);
-
+#if 0
 	ath_rate_newassoc(sc, an, isnew);
+#endif
 
+	DPRINTF(sc, ATH_DEBUG_NODE, "%s: newassoc is new: %d\n",
+			__func__, isnew);
+	if (isnew)
+		ieee80211_ratectl_node_init(ni);
+
 	if (isnew &&
 	    (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey &&
 	    ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFnsE3fnApV_=FNggEuhy1B=idXLxgymqqmJ=9XXrjspLoe%2BWA>