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