Date: Tue, 3 Sep 2013 01:54:33 +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: <CAFnsE3dw5bOyVRqMb9K3ME%2BHoDL61-e_XNJjjt_nRTaY=4dnhw@mail.gmail.com> In-Reply-To: <CAJ-Vmo=D3BNhhSn9D3x6hBry=fDecOd7BwttQ5Yw2iUKGDBN3w@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>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
Hi!
Attachment is my latest update. In this update, I teach ieee80211_amrr to
return
multiple rates and modify ath to let it use net80211_ratectl.
I realized that 2 tasks must be done before I can test the modified ratectl
api on
my AR9227 device. that is:
1) get a ratectl algo ready to use. ieee80211_amrr is choosed here. I
modified it to
fit the updated net80211_ratectl. That includes making it return multiple
rates and
use a unified ieee80211_rc_info to do the __complete__.
2) modified ath driver to let it use ieee80211_ratectl. We know that ath
uses it's own
rate control code to do the rate decisions and rc flags setups. So
ieee80211_ratectl
need to take over process of rate lookup and other related stuffs.
During my first attempt modifing ieee80211_amrr, I try to make it look
similiar to
ath_amrr. But, latter I found that perhaps we don't need to. As to amrr,
once we get
the first rix, we get the others. So we don't need to record lots of states
like
amn_tx_rate[0|1|2|3]. And, I found that, ath_amrr is quite confused. It
seems that it
return the ratecode as rix. rix should be the ratetable index, right? I've
saw that the rix
is used to index ratetable.
The modifications to ath is really strait forward. I just push
net80211_ratectl into ath
and copy ieee80211_rc_series to bf->bf_state.bfs_rc after we get the rates.
I also add a 48 bytes buf to mbuf (actually to m_hdr). That is where we
store ratectl
state ieee80211_rc_info for a frame. The struct mbuf is so elegant that I
really don't
want to add any thing to it. If you know some other places that is fit to
store the ratectl
state, please let me know. Thanks!
I compiled my latest kernel and tried it. A kernel panic with message "not
mcs" occured.
Perhaps my modifications to ieee80211_amrr caused it. I'll check it.
Thanks!
Chenchong
On Tue, Aug 27, 2013 at 5:02 PM, Adrian Chadd <adrian@freebsd.org> wrote:
> Sweet! Let me know how it goes.
>
>
>
> -adrian
>
>
>
> On 25 August 2013 23:43, Chenchong Qin <qinchenchong@gmail.com> wrote:
>
>> Hi!
>>
>> I struggled to perform changes to all parts that affected by my update to
>> ratectl api
>> and got the customized kernel compiled... I'm now tring to play it on my
>> AR9227 device.
>>
>> Thanks!
>>
>> Chenchong
>>
>>
>> On Sun, Aug 25, 2013 at 10:39 PM, Adrian Chadd <adrian@freebsd.org>wrote:
>>
>>> Hi!
>>>
>>> Have you tried this out with any hardware just yet?
>>>
>>>
>>> -adrian
>>>
>>>
>>>
>>> On 25 August 2013 07:30, Chenchong Qin <qinchenchong@gmail.com> wrote:
>>>
>>>> Hi!
>>>>
>>>> This is the latest update.
>>>>
>>>> * add a simple per vap ratectl statistic tracker and api to update it.
>>>> * port irn_capabilities to irs_capabilities in struct ieee80211_rc_stat.
>>>> perhaps the capabilities field needs to be within ieee80211_rc_stat as
>>>> a per vap atrribute. corresponding updates performed.
>>>> * add ieee80211_ratectl_none.h to record common ratectl state.
>>>>
>>>> Thanks!
>>>>
>>>> Chenchong
>>>>
>>>>
>>>> On Thu, Aug 15, 2013 at 8:03 PM, Chenchong Qin <qinchenchong@gmail.com>wrote:
>>>>
>>>>> Hi!
>>>>>
>>>>> Here is my latest update. In this update:
>>>>>
>>>>> * add a new struct, ieee80211_ratectl_node. This is the common state
>>>>> that all per node rc
>>>>> state, i.e. ieee80211_[amrr|sample]_node, should contains it as the
>>>>> first field. It's now used to store
>>>>> the capabilities. see below.
>>>>> * rename ir_capabilities to irn_capabilities and move it to
>>>>> ieee80211_ratectl_node (it contained in
>>>>> ieee80211_[amrr|sample]_node). ieee80211_ratectl is readonly, so
>>>>> ir_capabilities can't be set. And,
>>>>> the capabilities is not a part of rc algo. It seems that it should
>>>>> be put in the per node rc state. Interface
>>>>> of ieee80211_ratectl_node_init() and its callers are updated.
>>>>> References to ir_capabilities are also adapted.
>>>>> * add ieee80211_ratectl_[node_is11n|get_rateset] to the ratectl api.
>>>>> rc algoes all need these functions.
>>>>> * change the naming conversion of IEEE80211_RATECTL_FLAG_*.
>>>>> * some errors fixed.
>>>>>
>>>>>
>>>>> On Thu, Aug 15, 2013 at 12:58 AM, Adrian Chadd <adrian@freebsd.org>wrote:
>>>>>
>>>>>> Hi!
>>>>>>
>>>>>> So yes, we do need to have a generic way of returning that completion
>>>>>> information to the rate control code.
>>>>>>
>>>>>> I'm all for you churning the rate control API to return a struct
>>>>>> ieee80211_rc_info in the complete function and totally just kill arg1/arg2.
>>>>>> That forces us to extend ieee80211_rc_info to be "right" for all the
>>>>>> drivers.
>>>>>>
>>>>>
>>>>> Do you mean drop arg1/arg2 and pass pointer of ieee80211_rc_info to
>>>>> the complete function directly? Or return it
>>>>> when complete function return?
>>>>>
>>>>>
>>>>>> What wifi devices do you have there? It looks like we're almost at
>>>>>> the point where we can start converting a few things to use the modified
>>>>>> rate control API and modules - notably iwn (which won't use the multi-rate
>>>>>> retry stuff to begin with - it works "differently"..) and ath (which will
>>>>>> use the multi-rate retry stuff and the sample rate control module.)
>>>>>>
>>>>>
>>>>> Yeah, I have an AR9227 device at hand.
>>>>>
>>>>> And, I also get a question here. The ieee80211_ratetable doesn't get a
>>>>> rateCode field. So, how we get the
>>>>> ratecode of the non_ht rate?
>>>>>
>>>>> Thanks!
>>>>>
>>>>> Chenchong
>>>>>
>>>>>
>>>>
>>>
>>
>
[-- Attachment #2 --]
Index: sys/sys/mbuf.h
===================================================================
--- sys/sys/mbuf.h (revision 254826)
+++ sys/sys/mbuf.h (working copy)
@@ -95,6 +95,7 @@
caddr_t mh_data; /* location of data */
int mh_len; /* amount of data in this mbuf */
int mh_flags; /* flags; see below */
+ uint8_t mh_ccb[48]; /* common control block */
short mh_type; /* type of data in this mbuf */
uint8_t pad[M_HDR_PAD];/* word align */
};
@@ -173,6 +174,7 @@
#define m_type m_hdr.mh_type
#define m_flags m_hdr.mh_flags
#define m_nextpkt m_hdr.mh_nextpkt
+#define m_ccb m_hdr.mh_ccb
#define m_act m_nextpkt
#define m_pkthdr M_dat.MH.MH_pkthdr
#define m_ext M_dat.MH.MH_dat.MH_ext
Index: sys/net80211/ieee80211_ratectl.c
===================================================================
--- sys/net80211/ieee80211_ratectl.c (revision 254826)
+++ sys/net80211/ieee80211_ratectl.c (working copy)
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +50,40 @@
MALLOC_DEFINE(M_80211_RATECTL, "80211ratectl", "802.11 rate control");
+enum {
+ MCS_HT20,
+ MCS_HT20_SGI,
+ MCS_HT40,
+ MCS_HT40_SGI,
+};
+
+int max_4ms_framelen[4][32] = {
+ [MCS_HT20] = {
+ 3212, 6432, 9648, 12864, 19300, 25736, 28952, 32172,
+ 6424, 12852, 19280, 25708, 38568, 51424, 57852, 64280,
+ 9628, 19260, 28896, 38528, 57792, 65532, 65532, 65532,
+ 12828, 25656, 38488, 51320, 65532, 65532, 65532, 65532,
+ },
+ [MCS_HT20_SGI] = {
+ 3572, 7144, 10720, 14296, 21444, 28596, 32172, 35744,
+ 7140, 14284, 21428, 28568, 42856, 57144, 64288, 65532,
+ 10700, 21408, 32112, 42816, 64228, 65532, 65532, 65532,
+ 14256, 28516, 42780, 57040, 65532, 65532, 65532, 65532,
+ },
+ [MCS_HT40] = {
+ 6680, 13360, 20044, 26724, 40092, 53456, 60140, 65532,
+ 13348, 26700, 40052, 53400, 65532, 65532, 65532, 65532,
+ 20004, 40008, 60016, 65532, 65532, 65532, 65532, 65532,
+ 26644, 53292, 65532, 65532, 65532, 65532, 65532, 65532,
+ },
+ [MCS_HT40_SGI] = {
+ 7420, 14844, 22272, 29696, 44544, 59396, 65532, 65532,
+ 14832, 29668, 44504, 59340, 65532, 65532, 65532, 65532,
+ 22232, 44464, 65532, 65532, 65532, 65532, 65532, 65532,
+ 29616, 59232, 65532, 65532, 65532, 65532, 65532, 65532,
+ }
+};
+
void
ieee80211_ratectl_register(int type, const struct ieee80211_ratectl *ratectl)
{
@@ -66,11 +101,11 @@
}
void
-ieee80211_ratectl_init(struct ieee80211vap *vap)
+ieee80211_ratectl_init(struct ieee80211vap *vap, uint32_t capabilities)
{
if (vap->iv_rate == ratectls[IEEE80211_RATECTL_NONE])
ieee80211_ratectl_set(vap, IEEE80211_RATECTL_AMRR);
- vap->iv_rate->ir_init(vap);
+ vap->iv_rate->ir_init(vap, capabilities);
}
void
@@ -90,3 +125,106 @@
}
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);
+
+ }
+}
+
Index: sys/net80211/ieee80211_ratectl.h
===================================================================
--- sys/net80211/ieee80211_ratectl.h (revision 254826)
+++ sys/net80211/ieee80211_ratectl.h (working copy)
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,6 +25,8 @@
*
* $FreeBSD$
*/
+#ifndef _NET80211_IEEE80211_RATECTL_H_
+#define _NET80211_IEEE80211_RATECTL_H_
enum ieee80211_ratealgs {
IEEE80211_RATECTL_AMRR = 0,
@@ -37,18 +40,100 @@
#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; /* for now, just records short preamble */
+
+ /* TX info */
+ uint8_t iri_txcnt; /* TX count */
+ uint8_t iri_okcnt; /* TX ok with or without retry */
+ uint8_t iri_failcnt; /* TX retry-fail count */
+ 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(_m) ((struct ieee80211_rc_info *)(_m)->m_ccb)
+
+/*
+ * 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 *);
@@ -57,8 +142,10 @@
void ieee80211_ratectl_register(int, const struct ieee80211_ratectl *);
void ieee80211_ratectl_unregister(int);
-void ieee80211_ratectl_init(struct ieee80211vap *);
+void ieee80211_ratectl_init(struct ieee80211vap *, uint32_t);
void ieee80211_ratectl_set(struct ieee80211vap *, int);
+void ieee80211_ratectl_complete_rcflags(struct ieee80211_node *,
+ struct ieee80211_rc_info*);
MALLOC_DECLARE(M_80211_RATECTL);
@@ -93,10 +180,19 @@
}
static void __inline
+ieee80211_ratectl_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
+{
+ const struct ieee80211vap *vap = ni->ni_vap;
+
+ vap->iv_rate->ir_rates(ni, rc_info);
+ ieee80211_ratectl_complete_rcflags(ni, rc_info);
+}
+
+static void __inline
ieee80211_ratectl_tx_complete(const struct ieee80211vap *vap,
- const struct ieee80211_node *ni, int status, void *arg1, void *arg2)
+ 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 +211,89 @@
return;
vap->iv_rate->ir_setinterval(vap, msecs);
}
+
+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)
+{
+ return ieee80211_ratectl_node_is11n(ni) ?
+ (struct ieee80211_rateset *) &ni->ni_htrates :
+ &ni->ni_rates;
+}
+
+static void __inline
+ieee80211_ratectl_update_stat(const struct ieee80211vap *vap,
+ const struct ieee80211_rc_info *rc_info)
+{
+ struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+ irs->irs_txcnt += rc_info->iri_txcnt;
+ irs->irs_failcnt += rc_info->iri_failcnt;
+ irs->irs_retrycnt += rc_info->iri_retrycnt;
+ irs->irs_shortretry += rc_info->iri_shortretry;
+ irs->irs_longretry += rc_info->iri_longretry;
+}
+
+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;
+}
+
+#endif
Index: sys/net80211/ieee80211_ratectl_none.c
===================================================================
--- sys/net80211/ieee80211_ratectl_none.c (revision 254826)
+++ sys/net80211/ieee80211_ratectl_none.c (working copy)
@@ -44,10 +44,26 @@
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_ratectl_none.h>
static void
-none_init(struct ieee80211vap *vap)
+none_init(struct ieee80211vap *vap, uint32_t capabilities)
{
+ struct ieee80211_node *none;
+
+ KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
+
+ none = vap->iv_rs = malloc(sizeof(struct ieee80211_none),
+ M_80211_RATECTL, M_NOWAIT|M_ZERO);
+ if (none == NULL) {
+ if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
+ return;
+ }
+
+ struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+ irs->irs_capabilities = capabilities;
+
+ /* ... */
}
static void
@@ -59,6 +75,25 @@
static void
none_node_init(struct ieee80211_node *ni)
{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_none *none = vap->iv_rs;
+ struct ieee80211_none_node *non;
+
+ if (ni->ni_rctls == NULL) {
+ ni->ni_rctls = non = malloc(sizeof(struct ieee80211_none_node),
+ M_80211_RATECTL, M_NOWAIT|M_ZERO);
+ if (non == NULL) {
+ if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
+ "structure\n");
+ return;
+ }
+ } else
+ non = ni->ni_rctls;
+
+ non->non_none = none;
+
+ /* ... */
+
ni->ni_txrate = ni->ni_rates.rs_rates[0] & IEEE80211_RATE_VAL;
}
@@ -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: sys/net80211/ieee80211_rssadapt.c
===================================================================
--- sys/net80211/ieee80211_rssadapt.c (revision 254826)
+++ sys/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 *);
@@ -121,7 +121,7 @@
}
static void
-rssadapt_init(struct ieee80211vap *vap)
+rssadapt_init(struct ieee80211vap *vap, uint32_t capabilities)
{
struct ieee80211_rssadapt *rs;
@@ -134,6 +134,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);
Index: sys/net80211/ieee80211_rssadapt.h
===================================================================
--- sys/net80211/ieee80211_rssadapt.h (revision 254826)
+++ sys/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: sys/net80211/ieee80211_amrr.c
===================================================================
--- sys/net80211/ieee80211_amrr.c (revision 254826)
+++ sys/net80211/ieee80211_amrr.c (working copy)
@@ -47,8 +47,8 @@
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ht.h>
+#include <net80211/ieee80211_ratectl.h>
#include <net80211/ieee80211_amrr.h>
-#include <net80211/ieee80211_ratectl.h>
#define is_success(amn) \
((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
@@ -58,7 +58,7 @@
((amn)->amn_txcnt > 10)
static void amrr_setinterval(const struct ieee80211vap *, int);
-static void amrr_init(struct ieee80211vap *);
+static void amrr_init(struct ieee80211vap *, uint32_t);
static void amrr_deinit(struct ieee80211vap *);
static void amrr_node_init(struct ieee80211_node *);
static void amrr_node_deinit(struct ieee80211_node *);
@@ -65,9 +65,9 @@
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 *);
+ const struct ieee80211_node *, struct ieee80211_rc_info *);
static void amrr_tx_update(const struct ieee80211vap *vap,
const struct ieee80211_node *, void *, void *, void *);
static void amrr_sysctlattach(struct ieee80211vap *,
@@ -85,6 +85,7 @@
.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,
@@ -105,7 +106,7 @@
}
static void
-amrr_init(struct ieee80211vap *vap)
+amrr_init(struct ieee80211vap *vap, uint32_t capabilities)
{
struct ieee80211_amrr *amrr;
@@ -117,6 +118,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 +134,6 @@
free(vap->iv_rs, M_80211_RATECTL);
}
-static int
-amrr_node_is_11n(struct ieee80211_node *ni)
-{
-
- if (ni->ni_chan == NULL)
- return (0);
- if (ni->ni_chan == IEEE80211_CHAN_ANYC)
- return (0);
- return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
-}
-
static void
amrr_node_init(struct ieee80211_node *ni)
{
@@ -159,6 +153,7 @@
}
} else
amn = ni->ni_rctls;
+
amn->amn_amrr = amrr;
amn->amn_success = 0;
amn->amn_recovery = 0;
@@ -165,23 +160,13 @@
amn->amn_txcnt = amn->amn_retrycnt = 0;
amn->amn_success_threshold = amrr->amrr_min_success_threshold;
- /* 11n or not? Pick the right rateset */
- if (amrr_node_is_11n(ni)) {
- /* XXX ew */
- IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
- "%s: 11n node", __func__);
- rs = (struct ieee80211_rateset *) &ni->ni_htrates;
- } else {
- IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
- "%s: non-11n node", __func__);
- rs = &ni->ni_rates;
- }
+ rs = ieee80211_ratectl_get_rateset(ni);
/* Initial rate - lowest */
rate = rs->rs_rates[0];
/* XXX clear the basic rate flag if it's not 11n */
- if (! amrr_node_is_11n(ni))
+ if (! ieee80211_ratectl_node_is11n(ni))
rate &= IEEE80211_RATE_VAL;
/* pick initial rate from the rateset - HT or otherwise */
@@ -189,7 +174,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 +183,7 @@
}
/* if the rate is an 11n rate, ensure the MCS bit is set */
- if (amrr_node_is_11n(ni))
+ if (ieee80211_ratectl_node_is11n(ni))
rate |= IEEE80211_RATE_MCS;
/* Assign initial rate from the rateset */
@@ -226,14 +211,9 @@
KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
- /* 11n or not? Pick the right rateset */
- if (amrr_node_is_11n(ni)) {
- /* XXX ew */
- rs = (struct ieee80211_rateset *) &ni->ni_htrates;
- } else {
- rs = &ni->ni_rates;
- }
+ rs = ieee80211_ratectl_get_rateset(ni);
+
IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
"AMRR: current rate %d, txcnt=%d, retrycnt=%d",
rs->rs_rates[rix] & IEEE80211_RATE_VAL,
@@ -304,13 +284,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 +292,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 +304,66 @@
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);
+
+ 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 = 0;
+ }
+ if (--rix >= 0) {
+ code = ieee80211_ratectl_node_is11n(ni)? MCS(rix) : RATE(rix);
+ rc[2].rix = rt->rateCodeToIndex[code];
+ } else {
+ rc[2].rix = 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 = 0;
+ }
+ } else {
+ rc[0].tries = IEEE80211_RATECTL_TXMAXTRY;
+
+ rc[1].tries = 0;
+ rc[2].tries = 0;
+ rc[3].tries = 0;
+ rc[1].rix = 0;
+ rc[2].rix = 0;
+ rc[3].rix = 0;
+ }
+ }
+#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 +371,16 @@
*/
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;
}
/*
Index: sys/net80211/ieee80211_amrr.h
===================================================================
--- sys/net80211/ieee80211_amrr.h (revision 254826)
+++ sys/net80211/ieee80211_amrr.h (working copy)
@@ -20,6 +20,7 @@
#ifndef _NET80211_IEEE80211_AMRR_H_
#define _NET80211_IEEE80211_AMRR_H_
+#include <net80211/ieee80211_ratectl.h>
/*-
* Naive implementation of the Adaptive Multi Rate Retry algorithm:
*
@@ -35,6 +36,7 @@
struct ieee80211vap;
struct ieee80211_amrr {
+ struct ieee80211_rc_stat amrr_stat;
u_int amrr_min_success_threshold;
u_int amrr_max_success_threshold;
int amrr_interval; /* update interval (ticks) */
Index: sys/net80211/ieee80211_ratectl_none.h
===================================================================
--- sys/net80211/ieee80211_ratectl_none.h (revision 0)
+++ sys/net80211/ieee80211_ratectl_none.h (revision 256837)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _NET80211_IEEE80211_RATECTL_NONE_H_
+#define _NET80211_IEEE80211_RATECTL_NONE_H_
+
+#include <net80211/ieee80211_ratectl.h>
+
+/*
+ * Rate control settings.
+ */
+
+struct ieee80211_none {
+ struct ieee80211_rc_stat none_stat;
+ /* other stuffs */
+};
+
+/*
+ * Rate control state for a given node.
+ */
+struct ieee80211_none_node {
+ struct ieee80211_none *non_none;
+};
+
+#endif /* _NET80211_IEEE80211_RATECTL_NONE_H_ */
Index: sys/net80211/ieee80211_rc_sample.c
===================================================================
--- sys/net80211/ieee80211_rc_sample.c (revision 0)
+++ sys/net80211/ieee80211_rc_sample.c (revision 256837)
@@ -0,0 +1,1141 @@
+/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.c 248573 2013-02-27 04:33:06Z adrian $*/
+
+/*-
+ * Copyright (c) 2005 John Bicket
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include "opt_wlan.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_ht.h>
+#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_rc_sample.h>
+#include <net80211/ieee80211_rc_sample_txsched.h>
+
+static void sample_init(struct ieee80211vap *, uint32_t);
+static void sample_deinit(struct ieee80211vap *);
+static void sample_node_init(struct ieee80211_node *);
+static void sample_node_deinit(struct ieee80211_node *);
+static int sample_rate(struct ieee80211_node *, void *, uint32_t);
+static void sample_tx_complete(const struct ieee80211vap *,
+ const struct ieee80211_node *, int,
+ void *, void *);
+static void sample_tx_update(const struct ieee80211vap *vap,
+ const struct ieee80211_node *, void *, void *, void *);
+static void sample_setinterval(const struct ieee80211vap *, int);
+
+/* number of references from net80211 layer */
+static int nrefs = 0;
+
+static const struct ieee80211_ratectl sample = {
+ .ir_name = "sample",
+ .ir_attach = NULL,
+ .ir_detach = NULL,
+ .ir_init = sample_init,
+ .ir_deinit = sample_deinit,
+ .ir_node_init = sample_node_init,
+ .ir_node_deinit = sample_node_deinit,
+ .ir_rate = sample_rate,
+ .ir_rates = sample_rates,
+ .ir_tx_complete = sample_tx_complete,
+ .ir_tx_update = sample_tx_update,
+ .ir_setinterval = sample_setinterval,
+};
+IEEE80211_RATECTL_MODULE(sample, 1);
+IEEE80211_RATECTL_ALG(sample, IEEE80211_RATECTL_SAMPLE, sample);
+
+static void
+sample_init(struct ieee80211vap *vap, uint32_t capabilities)
+{
+ struct ieee80211_sample *sample;
+
+ KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
+
+ sample = vap->iv_rs = malloc(sizeof(struct ieee80211_sample),
+ M_80211_RATECTL, M_NOWAIT|M_ZERO);
+ if (sample == NULL) {
+ if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
+ return;
+ }
+
+ struct ieee80211_rc_stat * irs = IEEE80211_RATECTL_STAT(vap);
+ irs->irs_capabilities = capabilities;
+
+ sample->sample_smoothing_rate = 75; /* ewma percentage ([0..99]) */
+ sample->sample_smoothing_minpackets = 100 / (100 - sample->sample_smoothing_rate);
+ sample->sample_rate = 10; /* %time to try diff tx rates */
+ sample->sample_max_successive_failures = 3; /* threshold for rate sampling*/
+ sample->sample_stale_failure_timeout = 10 * hz; /* 10 seconds */
+ sample->sample_min_switch = hz; /* 1 second */
+ sample_setinterval(vap, 500 /* ms */); /* actually do nothing */
+ sample_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
+}
+
+static void
+sample_deinit(struct ieee80211vap *vap)
+{
+ free(vap->iv_rs, M_80211_RATECTL);
+}
+
+static const struct txschedule *mrr_schedules[IEEE80211_MODE_MAX+2] = {
+ NULL, /* IEEE80211_MODE_AUTO */
+ series_11a, /* IEEE80211_MODE_11A */
+ series_11g, /* IEEE80211_MODE_11B */
+ series_11g, /* IEEE80211_MODE_11G */
+ NULL, /* IEEE80211_MODE_FH */
+ series_11a, /* IEEE80211_MODE_TURBO_A */
+ series_11g, /* IEEE80211_MODE_TURBO_G */
+ series_11a, /* IEEE80211_MODE_STURBO_A */
+ series_11na, /* IEEE80211_MODE_11NA */
+ series_11ng, /* IEEE80211_MODE_11NG */
+ series_half, /* IEEE80211_MODE_HALF */
+ series_quarter, /* IEEE80211_MODE_QUARTER */
+};
+
+static void
+sample_node_init(struct ieee80211_node *ni)
+{
+#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+#define DOT11RATE(_ix) (rt->info[(_ix)].dot11Rate & IEEE80211_RATE_VAL)
+#define MCS(_ix) (ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_sample *sample = vap->iv_rs;
+ struct ieee80211_sample_node *san = NULL;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+ enum ieee80211_phymode curmode = ieee80211_chan2mode(vap->iv_ic->ic_curchan);
+ int x, y, rix;
+
+ if (ni->ni_rctls == NULL) {
+ ni->ni_rctls = san = malloc(sizeof(struct ieee80211_sample_node),
+ M_80211_RATECTL, M_NOWAIT|M_ZERO);
+ if (san == NULL) {
+ if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
+ "structure\n");
+ return;
+ }
+ } else
+ san = ni->ni_rctls;
+
+ san->san_sample = sample;
+
+ KASSERT(rt != NULL, ("no rate table, mode %u", curmode));
+
+ san->sched = mrr_schedules[curmode];
+ KASSERT(san->sched != NULL, ("no mrr schedule for mode %u", curmode));
+
+ san->static_rix = -1;
+ sample_update_static_rix(ni);
+
+ /*
+ * Construct a bitmask of usable rates. This has all
+ * negotiated rates minus those marked by the hal as
+ * to be ignored for doing rate control.
+ */
+ san->ratemask = 0;
+
+ /* MCS rates */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ for (x = 0; x < ni->ni_htrates.rs_nrates; x++) {
+ rix = rt->rateCodeToIndex[MCS(x)];
+ if (rix == 0xff)
+ continue;
+ /* skip rates marked broken by hal */
+ if (!rt->info[rix].valid)
+ continue;
+ KASSERT(rix < SAMPLE_MAXRATES,
+ ("mcs %u has rix %d", MCS(x), rix));
+ san->ratemask |= (uint64_t) 1<<rix;
+ }
+ }
+
+ /* Legacy rates */
+ for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+ rix = rt->rateCodeToIndex[RATE(x)];
+ if (rix == 0xff)
+ continue;
+ /* skip rates marked broken by hal */
+ if (!rt->info[rix].valid)
+ continue;
+ KASSERT(rix < SAMPLE_MAXRATES,
+ ("rate %u has rix %d", RATE(x), rix));
+ san->ratemask |= (uint64_t) 1<<rix;
+ }
+
+ for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+ int size = bin_to_size(y);
+ uint64_t mask;
+
+ san->packets_sent[y] = 0;
+ san->current_sample_rix[y] = -1;
+ san->last_sample_rix[y] = 0;
+ /* XXX start with first valid rate */
+ san->current_rix[y] = ffs(san->ratemask)-1;
+
+ /*
+ * Initialize the statistics buckets; these are
+ * indexed by the rate code index.
+ */
+ for (rix = 0, mask = san->ratemask; mask != 0; rix++, mask >>= 1) {
+ if ((mask & 1) == 0) /* not a valid rate */
+ continue;
+ san->stats[y][rix].successive_failures = 0;
+ san->stats[y][rix].tries = 0;
+ san->stats[y][rix].total_packets = 0;
+ san->stats[y][rix].packets_acked = 0;
+ san->stats[y][rix].last_tx = 0;
+ san->stats[y][rix].ewma_pct = 0;
+
+ san->stats[y][rix].perfect_tx_time =
+ calc_usecs_unicast_packet(vap, size, rix, 0, 0,
+ (ni->ni_chw == 40));
+ san->stats[y][rix].average_tx_time =
+ san->stats[y][rix].perfect_tx_time;
+ }
+ }
+
+ /* set the visible bit-rate */
+ if (san->static_rix != -1)
+ ni->ni_txrate = DOT11RATE(san->static_rix);
+ else
+ ni->ni_txrate = RATE(0);
+#undef RATE
+#undef DOT11RATE
+#undef MCS
+}
+
+static void
+sample_node_deinit(struct ieee80211_node *ni)
+{
+ free(ni->ni_rctls, M_80211_RATECTL);
+}
+
+static int
+dot11rate(const ieee80211_rate_table *rt, int rix)
+{
+ if (rix < 0)
+ return -1;
+ return rt->info[rix].phy == IEEE80211_T_HT ?
+ rt->info[rix].dot11Rate : (rt->info[rix].dot11Rate & IEEE80211_RATE_VAL) / 2;
+}
+
+static const char *
+dot11rate_label(const ieee80211_rate_table *rt, int rix)
+{
+ if (rix < 0)
+ return "";
+ return rt->info[rix].phy == IEEE80211_T_HT ? "MCS" : "Mb ";
+}
+
+/*
+ * Return the rix with the lowest average_tx_time,
+ * or -1 if all the average_tx_times are 0.
+ */
+static __inline int
+pick_best_rate(const struct ieee80211_node *ni, const struct ieee80211_rate_table *rt,
+ int size_bin, int require_acked_before)
+{
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ int best_rate_rix, best_rate_tt, best_rate_pct;
+ uint64_t mask;
+ int rix, tt, pct;
+
+ best_rate_rix = 0;
+ best_rate_tt = 0;
+ best_rate_pct = 0;
+
+ for (mask = san->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
+ if ((mask & 1) == 0) /* not a supported rate */
+ continue;
+
+ /* Don't pick a non-HT rate for a HT node */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ (rt->info[rix].phy != IEEE80211_T_HT)) {
+ continue;
+ }
+
+ tt = san->stats[size_bin][rix].average_tx_time;
+ if (tt <= 0 ||
+ (require_acked_before &&
+ !san->stats[size_bin][rix].packets_acked))
+ continue;
+
+ /* Calculate percentage if possible */
+ if (san->stats[size_bin][rix].total_packets > 0) {
+ pct = san->stats[size_bin][rix].ewma_pct;
+ } else {
+ /* XXX for now, assume 95% ok */
+ pct = 95;
+ }
+
+ /* don't use a bit-rate that has been failing */
+ if (san->stats[size_bin][rix].successive_failures > 3)
+ continue;
+
+ /*
+ * For HT, Don't use a bit rate that is much more
+ * lossy than the best.
+ *
+ * XXX this isn't optimal; it's just designed to
+ * eliminate rates that are going to be obviously
+ * worse.
+ */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (best_rate_pct > (pct + 50))
+ continue;
+ }
+
+ /*
+ * For non-MCS rates, use the current average txtime for
+ * comparison.
+ */
+ if (! (ni->ni_flags & IEEE80211_NODE_HT)) {
+ if (best_rate_tt == 0 || tt <= best_rate_tt) {
+ best_rate_tt = tt;
+ best_rate_rix = rix;
+ best_rate_pct = pct;
+ }
+ }
+
+ /*
+ * Since 2 stream rates have slightly higher TX times,
+ * allow a little bit of leeway. This should later
+ * be abstracted out and properly handled.
+ */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (best_rate_tt == 0 || (tt * 8 <= best_rate_tt * 10)) {
+ best_rate_tt = tt;
+ best_rate_rix = rix;
+ best_rate_pct = pct;
+ }
+ }
+ }
+ return (best_rate_tt ? best_rate_rix : -1);
+}
+
+/*
+ * Pick a good "random" bit-rate to sample other than the current one.
+ */
+static __inline int
+pick_sample_rate(const struct ieee80211_node *ni, const ieee80211_rate_table *rt,
+ int size_bin)
+{
+#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ struct ieee80211_sample *sample = san->san_sample;
+ int current_rix, rix;
+ unsigned current_tt;
+ uint64_t mask;
+
+ current_rix = san->current_rix[size_bin];
+ if (current_rix < 0) {
+ /* no successes yet, send at the lowest bit-rate */
+ /* XXX should return MCS0 if HT */
+ return 0;
+ }
+
+ current_tt = san->stats[size_bin][current_rix].average_tx_time;
+
+ rix = san->last_sample_rix[size_bin]+1; /* next sample rate */
+ mask = san->ratemask &~ ((uint64_t) 1<<current_rix);/* don't sample current rate */
+ while (mask != 0) {
+ if ((mask & ((uint64_t) 1<<rix)) == 0) { /* not a supported rate */
+ nextrate:
+ if (++rix >= rt->rateCount)
+ rix = 0;
+ continue;
+ }
+
+ /*
+ * The following code stops trying to sample
+ * non-MCS rates when speaking to an MCS node.
+ * However, at least for CCK rates in 2.4GHz mode,
+ * the non-MCS rates MAY actually provide better
+ * PER at the very far edge of reception.
+ *
+ * However! Until ath_rate_form_aggr() grows
+ * some logic to not form aggregates if the
+ * selected rate is non-MCS, this won't work.
+ *
+ * So don't disable this code until you've taught
+ * ath_rate_form_aggr() to drop out if any of
+ * the selected rates are non-MCS.
+ */
+#if 1
+ /* if the node is HT and the rate isn't HT, don't bother sample */
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ (rt->info[rix].phy != IEEE80211_T_HT)) {
+ mask &= ~((uint64_t) 1<<rix);
+ goto nextrate;
+ }
+#endif
+
+ /* this bit-rate is always worse than the current one */
+ if (san->stats[size_bin][rix].perfect_tx_time > current_tt) {
+ mask &= ~((uint64_t) 1<<rix);
+ goto nextrate;
+ }
+
+ /* rarely sample bit-rates that fail a lot */
+ if (san->stats[size_bin][rix].successive_failures > sample->sample_max_successive_failures &&
+ ticks - san->stats[size_bin][rix].last_tx < sample->sample_stale_failure_timeout) {
+ mask &= ~((uint64_t) 1<<rix);
+ goto nextrate;
+ }
+
+ /*
+ * For HT, only sample a few rates on either side of the
+ * current rix; there's quite likely a lot of them.
+ */
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (rix < (current_rix - 3) ||
+ rix > (current_rix + 3)) {
+ mask &= ~((uint64_t) 1<<rix);
+ goto nextrate;
+ }
+ }
+
+ /* Don't sample more than 2 rates higher for rates > 11M for non-HT rates */
+ if (! (ni->ni_flags & IEEE80211_NODE_HT)) {
+ if (DOT11RATE(rix) > 2*11 && rix > current_rix + 2) {
+ mask &= ~((uint64_t) 1<<rix);
+ goto nextrate;
+ }
+ }
+
+ san->last_sample_rix[size_bin] = rix;
+ return rix;
+ }
+ return current_rix;
+#undef DOT11RATE
+#undef MCS
+}
+
+static int
+sample_get_static_rix(const struct ieee80211_node *ni)
+{
+#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+#define MCS(_ix) (ni->ni_htrates.rs_rates[_ix] | IEEE80211_RATE_MCS)
+ struct ieee80211vap *vap = ni->ni_vap;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+ const struct ieee80211_txparam *tp = ni->ni_txparms;
+ int srate;
+
+ /* Check MCS rates */
+ for (srate = ni->ni_htrates.rs_nrates - 1; srate >= 0; srate--) {
+ if (MCS(srate) == tp->ucastrate)
+ return rt->rateCodeToIndex[tp->ucastrate];
+ }
+
+ /* Check legacy rates */
+ for (srate = ni->ni_rates.rs_nrates - 1; srate >= 0; srate--) {
+ if (RATE(srate) == tp->ucastrate)
+ return rt->rateCodeToIndex[tp->ucastrate];
+ }
+ return -1;
+#undef RATE
+#undef MCS
+}
+
+static void
+sample_update_static_rix(struct ieee80211_node *ni)
+{
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ const struct ieee80211_txparam *tp = ni->ni_txparms;
+
+ if (tp != NULL && tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ /*
+ * A fixed rate is to be used; ucastrate is the IEEE code
+ * for this rate (sans basic bit). Check this against the
+ * negotiated rate set for the node. Note the fixed rate
+ * may not be available for various reasons so we only
+ * setup the static rate index if the lookup is successful.
+ */
+ san->static_rix = sample_get_static_rix(ni);
+ } else {
+ san->static_rix = -1;
+ }
+}
+
+/*
+ * Pick a non-HT rate to begin using.
+ */
+static int
+sample_pick_seed_rate_legacy(const struct ieee80211_node *ni, int frameLen)
+{
+#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+
+ const int size_bin = size_to_bin(frameLen);
+ int rix = -1;
+
+ /* no packet has been sent successfully yet */
+ for (rix = rt->rateCount-1; rix > 0; rix--) {
+ if ((san->ratemask & ((uint64_t) 1<<rix)) == 0)
+ continue;
+
+ /* Skip HT rates */
+ if (rt->info[rix].phy == IEEE80211_T_HT)
+ continue;
+
+ /*
+ * Pick the highest rate <= 36 Mbps
+ * that hasn't failed.
+ */
+ if (DOT11RATE(rix) <= 72 &&
+ san->stats[size_bin][rix].successive_failures == 0) {
+ break;
+ }
+ }
+ return rix;
+#undef DOT11RATE
+}
+
+/*
+ * Pick a HT rate to begin using.
+ *
+ * Don't use any non-HT rates; only consider HT rates.
+ */
+static int
+sample_pick_seed_rate_ht(const struct ieee80211_node *ni, int frameLen)
+{
+#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+
+ const int size_bin = size_to_bin(frameLen);
+ int rix = -1, ht_rix = -1;
+
+ /* no packet has been sent successfully yet */
+ for (rix = rt->rateCount-1; rix > 0; rix--) {
+ /* Skip rates we can't use */
+ if ((san->ratemask & ((uint64_t) 1<<rix)) == 0)
+ continue;
+
+ /* Keep a copy of the last seen HT rate index */
+ if (rt->info[rix].phy == IEEE80211_T_HT)
+ ht_rix = rix;
+
+ /* Skip non-HT rates */
+ if (rt->info[rix].phy != IEEE80211_T_HT)
+ continue;
+
+ /*
+ * Pick a medium-speed rate regardless of stream count
+ * which has not seen any failures. Higher rates may fail;
+ * we'll try them later.
+ */
+ if (((MCS(rix) & 0x7) <= 4) &&
+ san->stats[size_bin][rix].successive_failures == 0) {
+ break;
+ }
+ }
+
+ /*
+ * If all the MCS rates have successive failures, rix should be
+ * > 0; otherwise use the lowest MCS rix (hopefully MCS 0.)
+ */
+ return MAX(rix, ht_rix);
+#undef MCS
+}
+
+static int
+sample_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
+{
+#define DOT11RATE(ix) (rt->info[ix].dot11Rate & IEEE80211_RATE_VAL)
+#define MCS(ix) (rt->info[ix].dot11Rate | IEEE80211_RATE_MCS)
+#define RATE(ix) (DOT11RATE(ix) / 2)
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ struct ieee80211_sample *sample = san->san_sample;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+
+ int rix, mrr, best_rix, change_rates;
+ unsigned average_tx_time;
+
+ size_t frameLen = (size_t)iarg;
+ const int size_bin = size_to_bin(frameLen);
+
+ sample_update_static_rix(ni);
+
+ if (san->static_rix != -1) {
+ rix = san->static_rix;
+ goto done;
+ }
+
+ if (IEEE80211_RATECTL_HASCAP_MRR(vap))
+ mrr = 1;
+ if (! IEEE80211_RATECTL_HASCAP_MRRPROT(vap))
+ mrr = 0;
+
+ best_rix = pick_best_rate(ni, rt, size_bin, !mrr);
+ if (best_rix >= 0) {
+ average_tx_time = san->stats[size_bin][best_rix].average_tx_time;
+ } else {
+ average_tx_time = 0;
+ }
+
+ /*
+ * Limit the time measuring the performance of other tx
+ * rates to sample_rate% of the total transmission time.
+ */
+ if (san->sample_tt[size_bin] < average_tx_time * (san->packets_since_sample[size_bin] *
+ sample->sample_rate/100)) {
+ rix = pick_sample_rate(ni, rt, size_bin);
+ IEEE80211_NOTE(vap, IEEE80211_MSG_RATECTL,
+ &ni, "att %d sample_tt %d size %u sample rate %d %s current rate %d %s",
+ average_tx_time,
+ san->sample_tt[size_bin],
+ bin_to_size(size_bin),
+ dot11rate(rt, rix),
+ dot11rate_label(rt, rix),
+ dot11rate(rt, san->current_rix[size_bin]),
+ dot11rate_label(rt, san->current_rix[size_bin]));
+ if (rix != san->current_rix[size_bin]) {
+ san->current_sample_rix[size_bin] = rix;
+ } else {
+ san->current_sample_rix[size_bin] = -1;
+ }
+ san->packets_since_sample[size_bin] = 0;
+ } else {
+ change_rates = 0;
+ if (!san->packets_sent[size_bin] || best_rix == -1) {
+ /* no packet has been sent successfully yet */
+ change_rates = 1;
+ if (ni->ni_flags & IEEE80211_NODE_HT)
+ best_rix = sample_pick_seed_rate_ht(ni, frameLen);
+ else
+ best_rix = sample_pick_seed_rate_legacy(ni, frameLen);
+ } else if (san->packets_sent[size_bin] < 20) {
+ /* let the bit-rate switch quickly during the first few packets */
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_RATECTL, &ni,
+ "%s: switching quickly..", __func__);
+ change_rates = 1;
+ } else if (ticks - sample->sample_min_switch > san->ticks_since_switch[size_bin]) {
+ /* min_switch seconds have gone by */
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_RATECTL, &ni,
+ "%s: min_switch %d > ticks_since_switch %d..",
+ __func__, ticks - sample->sample_min_switch, san->ticks_since_switch[size_bin]);
+ change_rates = 1;
+ } else if ((! (ni->ni_flags & IEEE80211_NODE_HT)) &&
+ (2*average_tx_time < san->stats[size_bin][san->current_rix[size_bin]].average_tx_time)) {
+ /* the current bit-rate is twice as slow as the best one */
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_RATECTL, &ni,
+ "%s: 2x att (= %d) < cur_rix att %d",
+ __func__,
+ 2 * average_tx_time, san->stats[size_bin][san->current_rix[size_bin]].average_tx_time);
+ change_rates = 1;
+ } else if ((ni->ni_flags & IEEE80211_NODE_HT)) {
+ int cur_rix = san->current_rix[size_bin];
+ int cur_att = san->stats[size_bin][cur_rix].average_tx_time;
+ /*
+ * If the node is HT, upgrade it if the MCS rate is
+ * higher and the average tx time is within 20% of
+ * the current rate. It can fail a little.
+ *
+ * This is likely not optimal!
+ */
+#if 0
+ printf("cur rix/att %x/%d, best rix/att %x/%d\n",
+ MCS(cur_rix), cur_att, MCS(best_rix), average_tx_time);
+#endif
+ if ((MCS(best_rix) > MCS(cur_rix)) &&
+ (average_tx_time * 8) <= (cur_att * 10)) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_RATECTL, &ni,
+ "%s: HT: best_rix 0x%d > cur_rix 0x%x, average_tx_time %d, cur_att %d",
+ __func__,
+ MCS(best_rix), MCS(cur_rix), average_tx_time, cur_att);
+ change_rates = 1;
+ }
+ }
+
+ san->packets_since_sample[size_bin]++;
+
+ if (change_rates) {
+ if (best_rix != san->current_rix[size_bin]) {
+ IEEE80211_NOTE(vap,
+ IEEE80211_MSG_RATECTL,
+ &ni,
+"%s: size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mrr %d",
+ __func__,
+ bin_to_size(size_bin),
+ RATE(san->current_rix[size_bin]),
+ san->stats[size_bin][san->current_rix[size_bin]].average_tx_time,
+ san->stats[size_bin][san->current_rix[size_bin]].perfect_tx_time,
+ RATE(best_rix),
+ san->stats[size_bin][best_rix].average_tx_time,
+ san->stats[size_bin][best_rix].perfect_tx_time,
+ san->packets_since_switch[size_bin],
+ mrr);
+ }
+ san->packets_since_switch[size_bin] = 0;
+ san->current_rix[size_bin] = best_rix;
+ san->ticks_since_switch[size_bin] = ticks;
+ /*
+ * Set the visible txrate for this node.
+ */
+ ni->ni_txrate = dot11rate(rt, best_rix);
+ }
+ rix = san->current_rix[size_bin];
+ san->packets_since_switch[size_bin]++;
+ }
+ // *try0 = mrr ? san->sched[rix].t0 : ATH_TXMAXTRY;
+done:
+
+ /*
+ * This bug totally sucks and should be fixed.
+ *
+ * For now though, let's not panic, so we can start to figure
+ * out how to better reproduce it.
+ */
+ if (rix < 0 || rix >= rt->rateCount) {
+ printf("%s: ERROR: rix %d out of bounds (rateCount=%d)\n",
+ __func__,
+ rix,
+ rt->rateCount);
+ rix = 0; /* XXX just default for now */
+ }
+ KASSERT(rix >= 0 && rix < rt->rateCount, ("rix is %d", rix));
+
+ // *rix0 = rix;
+ // *txrate = rt->info[rix].rateCode
+ // | (shortPreamble ? rt->info[rix].shortPreamble : 0);
+ san->packets_sent[size_bin]++;
+
+ return rix;
+#undef DOT11RATE
+#undef MCS
+#undef RATE
+}
+
+static void
+sample_rates(struct ieee80211_node *ni, struct ieee80211_rc_info *rc_info)
+{
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ uint8_t rix0 = sample_rate(ni, NULL, 0);
+ const struct txschedule *sched = &san->sched[rix0];
+ struct ieee80211_rc_series *rc = rc_info->iri_rc;
+
+ KASSERT(rix0 == sched->r0, ("rix0 (%x) != sched->r0 (%x)!\n",
+ rix0, sched->r0));
+ /* XXX */
+ rc[0].flags = rc[1].flags = rc[2].flags = rc[3].flags = 0;
+
+ rc[0].rix = sched->r0;
+ rc[1].rix = sched->r1;
+ rc[2].rix = sched->r2;
+ rc[3].rix = sched->r3;
+
+ rc[0].tries = sched->t0;
+ rc[1].tries = sched->t1;
+ rc[2].tries = sched->t2;
+ rc[3].tries = sched->t3;
+}
+
+static void
+update_stats(const struct ieee80211vap *vap,
+ const struct ieee80211_node *ni,
+ int frame_size,
+ int rix0, int tries0,
+ int rix1, int tries1,
+ int rix2, int tries2,
+ int rix3, int tries3,
+ int short_tries, int tries,
+ int nframes, int nbad)
+{
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ struct ieee80211_sample *sample = san->san_sample;
+
+ const int size_bin = size_to_bin(frame_size);
+ const int size = bin_to_size(size_bin);
+
+ int is_ht40 = ieee80211_ratectl_hascap_cw40(vap, ni);
+ int tt, tries_so_far;
+ int pct;
+
+ if (!IS_RATE_DEFINED(san, rix0))
+ return;
+ tt = calc_usecs_unicast_packet(vap, size, rix0, short_tries,
+ MIN(tries0, tries) - 1, is_ht40);
+ tries_so_far = tries0;
+
+ if (tries1 && tries_so_far < tries) {
+ if (!IS_RATE_DEFINED(san, rix1))
+ return;
+ tt += calc_usecs_unicast_packet(vap, size, rix1, short_tries,
+ MIN(tries1 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
+ tries_so_far += tries1;
+ }
+
+ if (tries2 && tries_so_far < tries) {
+ if (!IS_RATE_DEFINED(san, rix2))
+ return;
+ tt += calc_usecs_unicast_packet(vap, size, rix2, short_tries,
+ MIN(tries2 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
+ tries_so_far += tries2;
+ }
+
+ if (tries3 && tries_so_far < tries) {
+ if (!IS_RATE_DEFINED(san, rix3))
+ return;
+ tt += calc_usecs_unicast_packet(vap, size, rix3, short_tries,
+ MIN(tries3 + tries_so_far, tries) - tries_so_far - 1, is_ht40);
+ }
+
+ if (san->stats[size_bin][rix0].total_packets < sample->sanple_smoothing_minpackets) {
+ /* just average the first few packets */
+ int avg_tx = san->stats[size_bin][rix0].average_tx_time;
+ int packets = san->stats[size_bin][rix0].total_packets;
+ san->stats[size_bin][rix0].average_tx_time = (tt+(avg_tx*packets))/(packets+nframes);
+ } else {
+ /* use a ewma */
+ san->stats[size_bin][rix0].average_tx_time =
+ ((san->stats[size_bin][rix0].average_tx_time * sample->sanple_smoothing_rate) +
+ (tt * (100 - sample->sanple_smoothing_rate))) / 100;
+ }
+
+ /*
+ * XXX Don't mark the higher bit rates as also having failed; as this
+ * unfortunately stops those rates from being tasted when trying to
+ * TX. This happens with 11n aggregation.
+ */
+ if (nframes == nbad) {
+ san->stats[size_bin][rix0].successive_failures += nbad;
+
+ } else {
+ san->stats[size_bin][rix0].packets_acked += (nframes - nbad);
+ san->stats[size_bin][rix0].successive_failures = 0;
+ }
+ san->stats[size_bin][rix0].tries += tries;
+ san->stats[size_bin][rix0].last_tx = ticks;
+ san->stats[size_bin][rix0].total_packets += nframes;
+
+ /* update EWMA for this rix */
+
+ /* Calculate percentage based on current rate */
+ if (nframes == 0)
+ nframes = nbad = 1;
+ pct = ((nframes - nbad) * 1000) / nframes;
+
+ if (san->stats[size_bin][rix0].total_packets <
+ sample->sanple_smoothing_minpackets) {
+ /* just average the first few packets */
+ int a_pct = (san->stats[size_bin][rix0].packets_acked * 1000) /
+ (san->stats[size_bin][rix0].total_packets);
+ san->stats[size_bin][rix0].ewma_pct = a_pct;
+ } else {
+ /* use a ewma */
+ san->stats[size_bin][rix0].ewma_pct =
+ ((san->stats[size_bin][rix0].ewma_pct * sample->sanple_smoothing_rate) +
+ (pct * (100 - sample->sanple_smoothing_rate))) / 100;
+ }
+
+ if (rix0 == san->current_sample_rix[size_bin]) {
+ san->sample_tt[size_bin] = tt;
+ san->current_sample_rix[size_bin] = -1;
+ }
+}
+
+static void
+sample_tx_complete(const struct ieee80211vap *vap,
+ const struct ieee80211_node *ni, int ok,
+ void *arg1, void *arg2)
+{
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+
+ /* XXX need to change arg2 to pointer of ieee80211_rc_info */
+ struct ieee80211_rc_info *rc_info = (struct ieee80211_rc_info*)arg2;
+
+ int final_rix, short_tries, long_tries;
+ int nframes, nbad;
+ int frame_size, mrr;
+
+ /* update per vap statistics */
+ ieee80211_ratectl_update_stat(vap, rc_info);
+
+ final_rix = rt->rateCodeToIndex[rc_info->iri_txrate];
+ short_tries = rc_info->iri_shortretry;
+ /* XXX why plus 1 here? */
+ long_tries = rc_info->iri_longretry + 1;
+
+ nframes = rc_info->iri_txcnt;
+ nbad = rc_info->iri_failcnt;
+
+ frame_size = rc_info->iri_framelen;
+ mrr = 0;
+
+ if (nframes == 0) {
+ return;
+ }
+
+ if (frame_size == 0) /* NB: should not happen */
+ frame_size = 1500;
+
+ if (san->ratemask == 0) {
+ return;
+ }
+
+ if (IEEE80211_RATECTL_HASCAP_MRR(vap))
+ mrr = 1;
+ /* XXX check HT protmode too */
+ if (mrr && !IEEE80211_RATECTL_HASCAP_MRRPROT(vap))
+ mrr = 0;
+
+ if (!mrr || rc_info->iri_finaltsi == 0) {
+ if (!IS_RATE_DEFINED(san, final_rix)) {
+ return;
+ }
+ /*
+ * Only one rate was used; optimize work.
+ */
+ update_stats(vap, ni, frame_size,
+ final_rix, long_tries,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ short_tries, long_tries,
+ nframes, nbad);
+
+ } else {
+ int finalTSIdx = rc_info->iri_finaltsi;
+ int i;
+
+ /*
+ * NB: series > 0 are not penalized for failure
+ * based on the try counts under the assumption
+ * that losses are often bursty and since we
+ * sample higher rates 1 try at a time doing so
+ * may unfairly penalize them.
+ */
+ if (rc[0].tries) {
+ update_stats(vap, ni, frame_size,
+ rc[0].rix, rc[0].tries,
+ rc[1].rix, rc[1].tries,
+ rc[2].rix, rc[2].tries,
+ rc[3].rix, rc[3].tries,
+ short_tries, long_tries,
+ nframes, nbad);
+ long_tries -= rc[0].tries;
+ }
+
+ if (rc[1].tries && finalTSIdx > 0) {
+ update_stats(vap, ni, frame_size,
+ rc[1].rix, rc[1].tries,
+ rc[2].rix, rc[2].tries,
+ rc[3].rix, rc[3].tries,
+ 0, 0,
+ short_tries, long_tries,
+ nframes, nbad);
+ long_tries -= rc[1].tries;
+ }
+
+ if (rc[2].tries && finalTSIdx > 1) {
+ update_stats(vap, ni, frame_size,
+ rc[2].rix, rc[2].tries,
+ rc[3].rix, rc[3].tries,
+ 0, 0,
+ 0, 0,
+ short_tries, long_tries,
+ nframes, nbad);
+ long_tries -= rc[2].tries;
+ }
+
+ if (rc[3].tries && finalTSIdx > 2) {
+ update_stats(vap, ni, frame_size,
+ rc[3].rix, rc[3].tries,
+ 0, 0,
+ 0, 0,
+ 0, 0,
+ short_tries, long_tries,
+ nframes, nbad);
+ }
+ }
+}
+
+static void
+sample_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni,
+ void *arg1, void *arg2, void *arg3)
+{
+ /* nothing here. */
+}
+
+static void
+sample_setinterval(const struct ieee80211vap *vap, int msecs)
+{
+ struct ieee80211_sample *sample = vap->iv_rs;
+ int t;
+
+ if (msecs < 100)
+ msecs = 100;
+ t = msecs_to_ticks(msecs);
+ /* ieee80211_sample doesn't have the sample_interval field by now */
+ // sample->sample_interval = (t < 1) ? 1 : t;
+}
+
+static void
+sample_stats(void *arg, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211_sample_node *san = ni->ni_rctls;
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+ uint64_t mask;
+ int rix, y;
+
+ printf("\n[%s] refcnt %d static_rix (%d %s) ratemask 0x%jx\n",
+ ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni),
+ dot11rate(rt, san->static_rix),
+ dot11rate_label(rt, san->static_rix),
+ (uintmax_t)san->ratemask);
+ for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+ printf("[%4u] cur rix %d (%d %s) since switch: packets %d ticks %u\n",
+ bin_to_size(y), san->current_rix[y],
+ dot11rate(rt, san->current_rix[y]),
+ dot11rate_label(rt, san->current_rix[y]),
+ san->packets_since_switch[y], san->ticks_since_switch[y]);
+ printf("[%4u] last sample (%d %s) cur sample (%d %s) packets sent %d\n",
+ bin_to_size(y),
+ dot11rate(rt, san->last_sample_rix[y]),
+ dot11rate_label(rt, san->last_sample_rix[y]),
+ dot11rate(rt, san->current_sample_rix[y]),
+ dot11rate_label(rt, san->current_sample_rix[y]),
+ san->packets_sent[y]);
+ printf("[%4u] packets since sample %d sample tt %u\n",
+ bin_to_size(y), san->packets_since_sample[y],
+ san->sample_tt[y]);
+ }
+ for (mask = san->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
+ if ((mask & 1) == 0)
+ continue;
+ for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+ if (san->stats[y][rix].total_packets == 0)
+ continue;
+ printf("[%2u %s:%4u] %8ju:%-8ju (%3d%%) (EWMA %3d.%1d%%) T %8ju F %4d avg %5u last %u\n",
+ dot11rate(rt, rix), dot11rate_label(rt, rix),
+ bin_to_size(y),
+ (uintmax_t) san->stats[y][rix].total_packets,
+ (uintmax_t) san->stats[y][rix].packets_acked,
+ (int) ((san->stats[y][rix].packets_acked * 100ULL) /
+ san->stats[y][rix].total_packets),
+ san->stats[y][rix].ewma_pct / 10,
+ san->stats[y][rix].ewma_pct % 10,
+ (uintmax_t) san->stats[y][rix].tries,
+ san->stats[y][rix].successive_failures,
+ san->stats[y][rix].average_tx_time,
+ ticks - san->stats[y][rix].last_tx);
+ }
+ }
+}
+
+static int
+sample_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211vap *vap = arg1;
+ struct ieee80211com *ic = vap->iv_ifp->if_l2com;
+ int error, v;
+
+ v = 0;
+ error = sysctl_handle_int(oidp, &v, 0, req);
+ if (error || !req->newptr)
+ return error;
+ ieee80211_iterate_nodes(&ic->ic_sta, sample_stats, NULL);
+ return 0;
+}
+
+static int
+sample_sysctl_smoothing_rate(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211vap *vap = arg1;
+ struct ieee80211_sample *sample = vap->iv_rs;
+ int rate, error;
+
+ rate = sample->sample_smoothing_rate;
+ error = sysctl_handle_int(oidp, &rate, 0, req);
+ if (error || !req->newptr)
+ return error;
+ if (!(0 <= rate && rate < 100))
+ return EINVAL;
+ sample->sample_smoothing_rate = rate;
+ sample->sample_smoothing_minpackets = 100 / (100 - rate);
+ return 0;
+}
+
+static int
+sample_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211vap *vap = arg1;
+ struct ieee80211_sample *sample = vap->iv_rs;
+ int rate, error;
+
+ rate = sample->sample_rate;
+ error = sysctl_handle_int(oidp, &rate, 0, req);
+ if (error || !req->newptr)
+ return error;
+ if (!(2 <= rate && rate <= 100))
+ return EINVAL;
+ sample->sample_rate = rate;
+ return 0;
+}
+
+static void
+sample_sysctlattach(struct ieee80211vap *vap,
+ struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
+{
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "sample_smoothing_rate", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+ sample_sysctl_smoothing_rate, "I",
+ "sample: smoothing rate for avg tx time (%%)");
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "sample_rate", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+ sample_sysctl_sample_rate, "I",
+ "sample: percent air time devoted to sampling new rates (%%)");
+ /* XXX max_successive_failures, stale_failure_timeout, min_switch */
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+ "sample_stats", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+ sample_sysctl_stats, "I", "sample: print statistics");
+}
Index: sys/net80211/ieee80211_rc_sample.h
===================================================================
--- sys/net80211/ieee80211_rc_sample.h (revision 0)
+++ sys/net80211/ieee80211_rc_sample.h (revision 256837)
@@ -0,0 +1,287 @@
+/* $FreeBSD: head/sys/dev/ath/ath_rate/sample/sample.h 240382 2012-08-15 07:10:10Z adrian $*/
+
+/*-
+ * Copyright (c) 2005 John Bicket
+ * Copyright (c) 2013 Chenchong Qin <ccqin@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ * of any contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#ifndef _NET80211_IEEE80211_RATECTL_SAMPLE_H_
+#define _NET80211_IEEE80211_RATECTL_SAMPLE_H_
+
+#include <net80211/ieee80211_ratectl.h>
+
+/*
+ * for now, we track performance for three different packet
+ * size buckets
+ */
+#define NUM_PACKET_SIZE_BINS 2
+#define SAMPLE_MAXRATES 64 /* NB: corresponds to hal info[32] */
+
+/*
+ * Rate control settings.
+ */
+struct ieee80211_sample {
+ struct ieee80211_rc_stat sample_stat;
+ int sample_smoothing_rate; /* ewma percentage [0..99] */
+ int sample_smoothing_minpackets;
+ int sample_rate; /* %time to try different tx rates */
+ int sample_max_successive_failures;
+ int sample_stale_failure_timeout; /* how long to honor max_successive_failures */
+ int sample_min_switch; /* min time between rate changes */
+ int sample_min_good_pct; /* min good percentage for a rate to be considered */
+};
+
+struct rate_stats {
+ unsigned average_tx_time;
+ int successive_failures;
+ uint64_t tries;
+ uint64_t total_packets; /* pkts total since assoc */
+ uint64_t packets_acked; /* pkts acked since assoc */
+ int ewma_pct; /* EWMA percentage */
+ unsigned perfect_tx_time; /* transmit time for 0 retries */
+ int last_tx;
+};
+
+struct txschedule {
+ uint8_t t0, r0; /* series 0: tries, rate code */
+ uint8_t t1, r1; /* series 1: tries, rate code */
+ uint8_t t2, r2; /* series 2: tries, rate code */
+ uint8_t t3, r3; /* series 3: tries, rate code */
+};
+
+/*
+ * Rate control state for a given node.
+ */
+/* XXX change naming conversion? */
+struct ieee80211_sample_node {
+ struct ieee80211_sample *san_sample;/* backpointer */
+ int static_rix; /* rate index of fixed tx rate */
+ uint64_t ratemask; /* bit mask of valid rate indices */
+ const struct txschedule *sched; /* tx schedule table */
+
+ struct rate_stats stats[NUM_PACKET_SIZE_BINS][SAMPLE_MAXRATES];
+ int last_sample_rix[NUM_PACKET_SIZE_BINS];
+
+ int current_sample_rix[NUM_PACKET_SIZE_BINS];
+ int packets_sent[NUM_PACKET_SIZE_BINS];
+
+ int current_rix[NUM_PACKET_SIZE_BINS];
+ int packets_since_switch[NUM_PACKET_SIZE_BINS];
+ unsigned ticks_since_switch[NUM_PACKET_SIZE_BINS];
+
+ int packets_since_sample[NUM_PACKET_SIZE_BINS];
+ unsigned sample_tt[NUM_PACKET_SIZE_BINS];
+};
+
+#define IS_RATE_DEFINED(san, rix) (((san)->ratemask & (1<<(rix))) != 0)
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+static const int packet_size_bins[NUM_PACKET_SIZE_BINS] = { 250, 1600 };
+
+static inline int
+bin_to_size(int index)
+{
+ return packet_size_bins[index];
+}
+
+static inline int
+size_to_bin(int size)
+{
+#if NUM_PACKET_SIZE_BINS > 1
+ if (size <= packet_size_bins[0])
+ return 0;
+#endif
+#if NUM_PACKET_SIZE_BINS > 2
+ if (size <= packet_size_bins[1])
+ return 1;
+#endif
+#if NUM_PACKET_SIZE_BINS > 3
+ if (size <= packet_size_bins[2])
+ return 2;
+#endif
+#if NUM_PACKET_SIZE_BINS > 4
+#error "add support for more packet sizes"
+#endif
+ return NUM_PACKET_SIZE_BINS-1;
+}
+
+static uint32_t sample_pkt_txtime(const struct ieee80211_rate_table *rt,
+ uint32_t frameLen, uint16_t rateix, int isht40, int isShortPreamble)
+{
+ uint8_t rc;
+ int numStreams;
+
+ rc = rt->info[rateix].rateCode;
+
+ /* Legacy rate? Return the old way */
+ if (! IS_HT_RATE(rc))
+ return ieee80211_compute_duration(rt, frameLen, rateix, isShortPreamble);
+
+ /* 11n frame - extract out the number of spatial streams */
+ numStreams = HT_RC_2_STREAMS(rc);
+ KASSERT(numStreams > 0 && numStreams <= 4,
+ ("number of spatial streams needs to be 1..3: MCS rate 0x%x!",
+ rateix));
+
+ return ieee80211_compute_duration_ht(frameLen, rc, numStreams, isht40, isShortPreamble);
+}
+
+#define WIFI_CW_MIN 31
+#define WIFI_CW_MAX 1023
+
+/*
+ * Calculate the transmit duration of a frame.
+ */
+static unsigned calc_usecs_unicast_packet(const struct ieee80211vap *vap,
+ int length,
+ int rix, int short_retries,
+ int long_retries, int is_ht40)
+{
+ const struct ieee80211_rate_table *rt = ieee80211_get_ratetable(vap->iv_ic->ic_curchan);
+ struct ieee80211com *ic = vap->iv_ic;
+ int curmode = ieee80211_chan2mode(vap->iv_ic->ic_curchan);
+
+ unsigned t_slot, t_difs, t_sifs;
+ int rts, cts;
+ int tt, x, cw, cix;
+
+ int tt = 0;
+ int x = 0;
+ int cw = WIFI_CW_MIN;
+
+ KASSERT(rt != NULL, ("no rate table, mode %u", curmode));
+
+ if (rix >= rt->rateCount) {
+ printf("bogus rix %d, max %u, mode %u\n",
+ rix, rt->rateCount, curmode);
+ return 0;
+ }
+ cix = rt->info[rix].controlRate;
+ /*
+ * XXX getting mac/phy level timings should be fixed for turbo
+ * rates, and there is probably a way to get this from the
+ * hal...
+ */
+ switch (rt->info[rix].phy) {
+ case IEEE80211_T_OFDM:
+ t_slot = 9;
+ t_sifs = 16;
+ t_difs = 28;
+ /* fall through */
+ case IEEE80211_T_TURBO:
+ t_slot = 9;
+ t_sifs = 8;
+ t_difs = 28;
+ break;
+ case IEEE80211_T_HT:
+ t_slot = 9;
+ t_sifs = 8;
+ t_difs = 28;
+ break;
+ case IEEE80211_T_DS:
+ /* fall through to default */
+ default:
+ /* pg 205 ieee.802.11.pdf */
+ t_slot = 20;
+ t_difs = 50;
+ t_sifs = 10;
+ }
+
+ rts = cts = 0;
+
+ if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ rt->info[rix].phy == IEEE80211_T_OFDM) {
+ if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ rts = 1;
+ else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ cts = 1;
+
+ int protrix;
+ if (curmode == IEEE80211_MODE_11G)
+ protrix = rt->rateCodeToIndex[2*2];
+ else
+ protrix = rt->rateCodeToIndex[2*1];
+ if (0xff == protrix)
+ protrix = 0;
+
+ cix = rt->info[protrix].controlRate;
+ }
+
+ if (0 /*length > ic->ic_rtsthreshold */) {
+ rts = 1;
+ }
+
+ if (rts || cts) {
+ int ctsrate;
+ int ctsduration = 0;
+
+ /* NB: this is intentionally not a runtime check */
+ KASSERT(cix < rt->rateCount,
+ ("bogus cix %d, max %u, mode %u\n", cix, rt->rateCount,
+ curmode));
+
+ ctsrate = rt->info[cix].rateCode | rt->info[cix].shortPreamble;
+ if (rts) /* SIFS + CTS */
+ ctsduration += rt->info[cix].spAckDuration;
+
+ /* XXX assumes short preamble */
+ ctsduration += sample_pkt_txtime(rt, length, rix, is_ht40, 0);
+
+ if (cts) /* SIFS + ACK */
+ ctsduration += rt->info[cix].spAckDuration;
+
+ tt += (short_retries + 1) * ctsduration;
+ }
+ tt += t_difs;
+
+ /* XXX assumes short preamble */
+ tt += (long_retries+1)*sample_pkt_txtime(rt, length, rix, is_ht40, 0);
+
+ tt += (long_retries+1)*(t_sifs + rt->info[rix].spAckDuration);
+
+ for (x = 0; x <= short_retries + long_retries; x++) {
+ cw = MIN(WIFI_CW_MAX, (cw + 1) * 2);
+ tt += (t_slot * cw/2);
+ }
+ return tt;
+}
+
+#endif /* _NET80211_IEEE80211_RATECTL_SAMPLE_H_ */
Index: sys/net80211/ieee80211_rc_sample_txsched.h
===================================================================
--- sys/net80211/ieee80211_rc_sample_txsched.h (revision 0)
+++ sys/net80211/ieee80211_rc_sample_txsched.h (revision 256837)
@@ -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: sys/dev/iwn/if_iwn.c
===================================================================
--- sys/dev/iwn/if_iwn.c (revision 254826)
+++ sys/dev/iwn/if_iwn.c (working copy)
@@ -876,7 +876,8 @@
ivp->iv_newstate = vap->iv_newstate;
vap->iv_newstate = iwn_newstate;
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* Complete setup. */
ieee80211_vap_attach(vap, iwn_media_change, ieee80211_media_status);
ic->ic_opmode = opmode;
@@ -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: sys/dev/bwi/if_bwi.c
===================================================================
--- sys/dev/bwi/if_bwi.c (revision 254826)
+++ sys/dev/bwi/if_bwi.c (working copy)
@@ -617,7 +617,8 @@
#if 0
vap->iv_update_beacon = bwi_beacon_update;
#endif
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* complete setup */
ieee80211_vap_attach(vap, bwi_media_change, ieee80211_media_status);
@@ -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: sys/dev/bwn/if_bwn.c
===================================================================
--- sys/dev/bwn/if_bwn.c (revision 254826)
+++ sys/dev/bwn/if_bwn.c (working copy)
@@ -2970,7 +2970,8 @@
/* override max aid so sta's cannot assoc when we're out of sta id's */
vap->iv_max_aid = BWN_STAID_MAX;
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change,
@@ -9020,11 +9021,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 +9045,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: sys/dev/usb/wlan/if_ural.c
===================================================================
--- sys/dev/usb/wlan/if_ural.c (revision 254826)
+++ sys/dev/usb/wlan/if_ural.c (working copy)
@@ -598,7 +598,8 @@
usb_callout_init_mtx(&uvp->ratectl_ch, &sc->sc_mtx, 0);
TASK_INIT(&uvp->ratectl_task, 0, ural_ratectl_task, uvp);
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
/* complete setup */
Index: sys/dev/usb/wlan/if_rum.c
===================================================================
--- sys/dev/usb/wlan/if_rum.c (revision 254826)
+++ sys/dev/usb/wlan/if_rum.c (working copy)
@@ -613,7 +613,8 @@
usb_callout_init_mtx(&rvp->ratectl_ch, &sc->sc_mtx, 0);
TASK_INIT(&rvp->ratectl_task, 0, rum_ratectl_task, rvp);
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
Index: sys/dev/usb/wlan/if_run.c
===================================================================
--- sys/dev/usb/wlan/if_run.c (revision 254826)
+++ sys/dev/usb/wlan/if_run.c (working copy)
@@ -820,7 +820,8 @@
rvp->newstate = vap->iv_newstate;
vap->iv_newstate = run_newstate;
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
/* complete setup */
Index: sys/dev/usb/wlan/if_zyd.c
===================================================================
--- sys/dev/usb/wlan/if_zyd.c (revision 254826)
+++ sys/dev/usb/wlan/if_zyd.c (working copy)
@@ -496,7 +496,8 @@
zvp->newstate = vap->iv_newstate;
vap->iv_newstate = zyd_newstate;
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
ieee80211_ratectl_setinterval(vap, 1000 /* 1 sec */);
/* complete setup */
@@ -667,9 +668,12 @@
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: sys/dev/wpi/if_wpi.c
===================================================================
--- sys/dev/wpi/if_wpi.c (revision 254826)
+++ sys/dev/wpi/if_wpi.c (working copy)
@@ -782,7 +782,8 @@
wvp->newstate = vap->iv_newstate;
vap->iv_newstate = wpi_newstate;
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
ic->ic_opmode = opmode;
@@ -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: sys/dev/ral/rt2560.c
===================================================================
--- sys/dev/ral/rt2560.c (revision 254826)
+++ sys/dev/ral/rt2560.c (working copy)
@@ -426,7 +426,8 @@
vap->iv_newstate = rt2560_newstate;
vap->iv_update_beacon = rt2560_beacon_update;
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
if (TAILQ_FIRST(&ic->ic_vaps) == vap)
@@ -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: sys/dev/ral/rt2661.c
===================================================================
--- sys/dev/ral/rt2661.c (revision 254826)
+++ sys/dev/ral/rt2661.c (working copy)
@@ -423,7 +423,8 @@
vap->iv_update_beacon = rt2661_beacon_update;
#endif
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
if (TAILQ_FIRST(&ic->ic_vaps) == vap)
@@ -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: sys/dev/ral/rt2860.c
===================================================================
--- sys/dev/ral/rt2860.c (revision 254826)
+++ sys/dev/ral/rt2860.c (working copy)
@@ -484,7 +484,8 @@
/* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */
vap->iv_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX);
- ieee80211_ratectl_init(vap);
+ /* XXX TODO fill the cap */
+ ieee80211_ratectl_init(vap, 0);
/* complete setup */
ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
if (TAILQ_FIRST(&ic->ic_vaps) == vap)
@@ -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: sys/dev/ath/if_ath_tx.c
===================================================================
--- sys/dev/ath/if_ath_tx.c (revision 254826)
+++ sys/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
@@ -1395,7 +1397,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 +1411,21 @@
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 */
+ struct ieee80211_rc_info *rc_info = IEEE80211_RATECTL_INFO(bf->bf_m);
+ struct ieee80211_rc_series *rc = rc_info->iri_rc;
+ struct ieee80211_node *ni = bf->bf_node;
+ bzero(rc_info, sizeof(rc_info));
+ if (bf->bf_state.bfs_shpream)
+ rc_info->iri_flags |= IEEE80211_RATECTL_INFO_SP;
+ 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;
@@ -4108,10 +4124,19 @@
* during a hw queue drain and the frame wanted an ACK.
*/
if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 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);
+ struct ieee80211_rc_info *rc_info = IEEE80211_RATECTL_INFO(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);
+ ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
+ }
+
ath_tx_default_comp(sc, bf, fail);
}
@@ -4490,6 +4515,14 @@
bf_first->bf_state.bfs_pktlen,
bf_first->bf_state.bfs_nframes, bf_first->bf_state.bfs_nframes);
+ struct ath_tx_status ts = bf_first->bf_status.ds_txstat;
+ struct ieee80211_rc_info *rc_info = IEEE80211_RATECTL_INFO(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);
+ ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
ATH_TX_LOCK(sc);
tap = ath_tx_get_tx_tid(an, tid->tid);
sc->sc_stats.ast_tx_aggr_failall++;
@@ -4873,9 +4906,18 @@
* control code.
*/
if (fail == 0)
+ {
ath_tx_update_ratectrl(sc, ni, rc, &ts, pktlen, nframes,
nbad);
+ struct ieee80211_rc_info *rc_info = IEEE80211_RATECTL_INFO(bf->bf_m);
+ ieee80211_ratectl_rc_info_set(rc_info,
+ nframes, nbad, pktlen,
+ ts.ts_shortretry, ts.ts_longretry,
+ ts.ts_finaltsi, ts.ts_rate);
+ ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
+ }
+
/*
* send bar if we dropped any frames
*/
@@ -4962,11 +5004,19 @@
* Do it outside of the TXQ lock.
*/
if (fail == 0 && ((bf->bf_state.bfs_txflags & HAL_TXDESC_NOACK) == 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);
+ struct ieee80211_rc_info *rc_info = IEEE80211_RATECTL_INFO(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);
+ 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
Index: sys/dev/ath/if_ath_tx_ht.c
===================================================================
--- sys/dev/ath/if_ath_tx_ht.c (revision 254826)
+++ sys/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,34 @@
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 = IEEE80211_RATECTL_INFO(bf->bf_m);
+ struct ieee80211_rc_series *rc = rc_info->iri_rc;
uint8_t rate;
int i;
+ 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;
+ }
+
+#if 0
for (i = 0; i < ATH_RC_NUM; i++) {
rc[i].flags = 0;
if (rc[i].tries == 0)
@@ -325,6 +348,7 @@
"%s: i=%d, rate=0x%x, flags=0x%x, max4ms=%d\n",
__func__, i, rate, rc[i].flags, rc[i].max4msframelen);
}
+#endif
}
/*
Index: sys/dev/ath/if_ath.c
===================================================================
--- sys/dev/ath/if_ath.c (revision 254826)
+++ sys/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
@@ -1380,6 +1382,15 @@
}
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 +1425,7 @@
ath_stoprecv(sc, 1); /* stop recv side */
}
+ ieee80211_ratectl_deinit(vap);
ieee80211_vap_detach(vap);
/*
@@ -4072,6 +4084,13 @@
bf->bf_state.bfs_rc, ts,
bf->bf_state.bfs_pktlen, 1,
(ts->ts_status == 0 ? 0 : 1));
+
+ struct ieee80211_rc_info *rc_info = IEEE80211_RATECTL_INFO(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);
+ ieee80211_ratectl_tx_complete(ni->ni_vap, ni, rc_info);
}
ath_tx_default_comp(sc, bf, 0);
} else
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAFnsE3dw5bOyVRqMb9K3ME%2BHoDL61-e_XNJjjt_nRTaY=4dnhw>
