174828f25SSam Leffler /* $OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $ */
274828f25SSam Leffler
374828f25SSam Leffler /*-
4b6108616SRui Paulo * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
574828f25SSam Leffler * Copyright (c) 2006
674828f25SSam Leffler * Damien Bergamini <damien.bergamini@free.fr>
774828f25SSam Leffler *
874828f25SSam Leffler * Permission to use, copy, modify, and distribute this software for any
974828f25SSam Leffler * purpose with or without fee is hereby granted, provided that the above
1074828f25SSam Leffler * copyright notice and this permission notice appear in all copies.
1174828f25SSam Leffler *
1274828f25SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1374828f25SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1474828f25SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1574828f25SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1674828f25SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1774828f25SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1874828f25SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1974828f25SSam Leffler */
2074828f25SSam Leffler
2174828f25SSam Leffler #include <sys/cdefs.h>
2274828f25SSam Leffler /*-
2374828f25SSam Leffler * Naive implementation of the Adaptive Multi Rate Retry algorithm:
2474828f25SSam Leffler *
2574828f25SSam Leffler * "IEEE 802.11 Rate Adaptation: A Practical Approach"
2674828f25SSam Leffler * Mathieu Lacage, Hossein Manshaei, Thierry Turletti
2774828f25SSam Leffler * INRIA Sophia - Projet Planete
2874828f25SSam Leffler * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
2974828f25SSam Leffler */
30b032f27cSSam Leffler #include "opt_wlan.h"
31b032f27cSSam Leffler
3274828f25SSam Leffler #include <sys/param.h>
3374828f25SSam Leffler #include <sys/kernel.h>
348ec07310SGleb Smirnoff #include <sys/malloc.h>
3574828f25SSam Leffler #include <sys/module.h>
3673931706SAdrian Chadd #include <sys/sbuf.h>
3774828f25SSam Leffler #include <sys/socket.h>
3874828f25SSam Leffler #include <sys/sysctl.h>
3974828f25SSam Leffler
4074828f25SSam Leffler #include <net/if.h>
4176039bc8SGleb Smirnoff #include <net/if_var.h>
4274828f25SSam Leffler #include <net/if_media.h>
43c3322cb9SGleb Smirnoff #include <net/ethernet.h>
4474828f25SSam Leffler
4574828f25SSam Leffler #ifdef INET
4674828f25SSam Leffler #include <netinet/in.h>
4774828f25SSam Leffler #include <netinet/if_ether.h>
4874828f25SSam Leffler #endif
4974828f25SSam Leffler
5074828f25SSam Leffler #include <net80211/ieee80211_var.h>
51f8bf74f2SAdrian Chadd #include <net80211/ieee80211_ht.h>
5274828f25SSam Leffler #include <net80211/ieee80211_amrr.h>
53b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h>
5474828f25SSam Leffler
5574828f25SSam Leffler #define is_success(amn) \
5674828f25SSam Leffler ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
5774828f25SSam Leffler #define is_failure(amn) \
5874828f25SSam Leffler ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
5974828f25SSam Leffler #define is_enough(amn) \
6074828f25SSam Leffler ((amn)->amn_txcnt > 10)
61b032f27cSSam Leffler
62b6108616SRui Paulo static void amrr_setinterval(const struct ieee80211vap *, int);
63b6108616SRui Paulo static void amrr_init(struct ieee80211vap *);
64b6108616SRui Paulo static void amrr_deinit(struct ieee80211vap *);
65b6108616SRui Paulo static void amrr_node_init(struct ieee80211_node *);
66b6108616SRui Paulo static void amrr_node_deinit(struct ieee80211_node *);
67b6108616SRui Paulo static int amrr_update(struct ieee80211_amrr *,
68b6108616SRui Paulo struct ieee80211_amrr_node *, struct ieee80211_node *);
69b6108616SRui Paulo static int amrr_rate(struct ieee80211_node *, void *, uint32_t);
70f6930becSAndriy Voskoboinyk static void amrr_tx_complete(const struct ieee80211_node *,
71f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *);
72f6930becSAndriy Voskoboinyk static void amrr_tx_update_cb(void *, struct ieee80211_node *);
73f6930becSAndriy Voskoboinyk static void amrr_tx_update(struct ieee80211vap *vap,
74f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *);
75b6108616SRui Paulo static void amrr_sysctlattach(struct ieee80211vap *,
76b6108616SRui Paulo struct sysctl_ctx_list *, struct sysctl_oid *);
7773931706SAdrian Chadd static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
78b032f27cSSam Leffler
79b032f27cSSam Leffler /* number of references from net80211 layer */
80b032f27cSSam Leffler static int nrefs = 0;
81b032f27cSSam Leffler
82b6108616SRui Paulo static const struct ieee80211_ratectl amrr = {
83b6108616SRui Paulo .ir_name = "amrr",
84b6108616SRui Paulo .ir_attach = NULL,
85b6108616SRui Paulo .ir_detach = NULL,
86b6108616SRui Paulo .ir_init = amrr_init,
87b6108616SRui Paulo .ir_deinit = amrr_deinit,
88b6108616SRui Paulo .ir_node_init = amrr_node_init,
89b6108616SRui Paulo .ir_node_deinit = amrr_node_deinit,
90b6108616SRui Paulo .ir_rate = amrr_rate,
91b6108616SRui Paulo .ir_tx_complete = amrr_tx_complete,
92b6108616SRui Paulo .ir_tx_update = amrr_tx_update,
93b6108616SRui Paulo .ir_setinterval = amrr_setinterval,
9473931706SAdrian Chadd .ir_node_stats = amrr_node_stats,
95b6108616SRui Paulo };
96b6108616SRui Paulo IEEE80211_RATECTL_MODULE(amrr, 1);
97b6108616SRui Paulo IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
98b6108616SRui Paulo
99b6108616SRui Paulo static void
amrr_setinterval(const struct ieee80211vap * vap,int msecs)100b6108616SRui Paulo amrr_setinterval(const struct ieee80211vap *vap, int msecs)
101b032f27cSSam Leffler {
102b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
103b032f27cSSam Leffler
1044367c2d1SAndriy Voskoboinyk if (!amrr)
1054367c2d1SAndriy Voskoboinyk return;
1064367c2d1SAndriy Voskoboinyk
107b032f27cSSam Leffler if (msecs < 100)
108b032f27cSSam Leffler msecs = 100;
1099df9e936SAndriy Voskoboinyk amrr->amrr_interval = msecs_to_ticks(msecs);
110b032f27cSSam Leffler }
11174828f25SSam Leffler
112b6108616SRui Paulo static void
amrr_init(struct ieee80211vap * vap)113b6108616SRui Paulo amrr_init(struct ieee80211vap *vap)
11474828f25SSam Leffler {
115b6108616SRui Paulo struct ieee80211_amrr *amrr;
116b032f27cSSam Leffler
117b6108616SRui Paulo KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
118b6108616SRui Paulo
119810490a0SAndriy Voskoboinyk nrefs++; /* XXX locking */
120b9b53389SAdrian Chadd amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
121b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
122645fe19aSRui Paulo if (amrr == NULL) {
123645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
124645fe19aSRui Paulo return;
125645fe19aSRui Paulo }
126b6108616SRui Paulo amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
127b6108616SRui Paulo amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
128b6108616SRui Paulo amrr_setinterval(vap, 500 /* ms */);
129b6108616SRui Paulo amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
130b032f27cSSam Leffler }
131b032f27cSSam Leffler
132b6108616SRui Paulo static void
amrr_deinit(struct ieee80211vap * vap)133b6108616SRui Paulo amrr_deinit(struct ieee80211vap *vap)
134b032f27cSSam Leffler {
135810490a0SAndriy Voskoboinyk KASSERT(nrefs > 0, ("imbalanced attach/detach"));
136cd9fee3dSBjoern A. Zeeb IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
137cd9fee3dSBjoern A. Zeeb vap->iv_rs = NULL; /* guard */
138810490a0SAndriy Voskoboinyk nrefs--; /* XXX locking */
13974828f25SSam Leffler }
14074828f25SSam Leffler
141b6108616SRui Paulo static void
amrr_node_init(struct ieee80211_node * ni)142b6108616SRui Paulo amrr_node_init(struct ieee80211_node *ni)
14374828f25SSam Leffler {
144f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL;
145b6108616SRui Paulo struct ieee80211vap *vap = ni->ni_vap;
146b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
147b6108616SRui Paulo struct ieee80211_amrr_node *amn;
148f8bf74f2SAdrian Chadd uint8_t rate;
149b032f27cSSam Leffler
1504367c2d1SAndriy Voskoboinyk if (!amrr) {
1514367c2d1SAndriy Voskoboinyk if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
1524367c2d1SAndriy Voskoboinyk "per-node structure allocation skipped\n");
1534367c2d1SAndriy Voskoboinyk return;
1544367c2d1SAndriy Voskoboinyk }
1554367c2d1SAndriy Voskoboinyk
156380fe2dfSRui Paulo if (ni->ni_rctls == NULL) {
157b9b53389SAdrian Chadd ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
158b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
159645fe19aSRui Paulo if (amn == NULL) {
160645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
161645fe19aSRui Paulo "structure\n");
162645fe19aSRui Paulo return;
163645fe19aSRui Paulo }
164380fe2dfSRui Paulo } else
165380fe2dfSRui Paulo amn = ni->ni_rctls;
166b032f27cSSam Leffler amn->amn_amrr = amrr;
16774828f25SSam Leffler amn->amn_success = 0;
16874828f25SSam Leffler amn->amn_recovery = 0;
16974828f25SSam Leffler amn->amn_txcnt = amn->amn_retrycnt = 0;
17074828f25SSam Leffler amn->amn_success_threshold = amrr->amrr_min_success_threshold;
171b032f27cSSam Leffler
172f8bf74f2SAdrian Chadd /* 11n or not? Pick the right rateset */
173*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
174f8bf74f2SAdrian Chadd /* XXX ew */
175f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
176f8bf74f2SAdrian Chadd "%s: 11n node", __func__);
177f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
178f8bf74f2SAdrian Chadd } else {
179f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
180f8bf74f2SAdrian Chadd "%s: non-11n node", __func__);
181f8bf74f2SAdrian Chadd rs = &ni->ni_rates;
182f8bf74f2SAdrian Chadd }
183f8bf74f2SAdrian Chadd
184f8bf74f2SAdrian Chadd /* Initial rate - lowest */
185f8bf74f2SAdrian Chadd rate = rs->rs_rates[0];
186f8bf74f2SAdrian Chadd
187f8bf74f2SAdrian Chadd /* XXX clear the basic rate flag if it's not 11n */
188*2014462dSAdrian Chadd if (! ieee80211_ht_check_tx_ht(ni))
189f8bf74f2SAdrian Chadd rate &= IEEE80211_RATE_VAL;
190f8bf74f2SAdrian Chadd
191f8bf74f2SAdrian Chadd /* pick initial rate from the rateset - HT or otherwise */
192ebb12408SAdrian Chadd /* Pick something low that's likely to succeed */
193f8bf74f2SAdrian Chadd for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
194f8bf74f2SAdrian Chadd amn->amn_rix--) {
195f8bf74f2SAdrian Chadd /* legacy - anything < 36mbit, stop searching */
196ebb12408SAdrian Chadd /* 11n - stop at MCS4 */
197*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
198ebb12408SAdrian Chadd if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
199f8bf74f2SAdrian Chadd break;
2005140f9e6SAdrian Chadd } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
201f8bf74f2SAdrian Chadd break;
202f8bf74f2SAdrian Chadd }
2035140f9e6SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
204f8bf74f2SAdrian Chadd
205f8bf74f2SAdrian Chadd /* if the rate is an 11n rate, ensure the MCS bit is set */
206*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni))
207f8bf74f2SAdrian Chadd rate |= IEEE80211_RATE_MCS;
208f8bf74f2SAdrian Chadd
209f8bf74f2SAdrian Chadd /* Assign initial rate from the rateset */
210f8bf74f2SAdrian Chadd ni->ni_txrate = rate;
211b032f27cSSam Leffler amn->amn_ticks = ticks;
212b032f27cSSam Leffler
213eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
214eec88845SAdrian Chadd /* XXX TODO: non-11n rate should be divided by two.. */
215b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
216eec88845SAdrian Chadd "AMRR: nrates=%d, initial rate %s%d",
217f8bf74f2SAdrian Chadd rs->rs_nrates,
218*2014462dSAdrian Chadd ieee80211_ht_check_tx_ht(ni) ? "MCS " : "",
219eec88845SAdrian Chadd rate & IEEE80211_RATE_VAL);
22074828f25SSam Leffler }
22174828f25SSam Leffler
222b6108616SRui Paulo static void
amrr_node_deinit(struct ieee80211_node * ni)223b6108616SRui Paulo amrr_node_deinit(struct ieee80211_node *ni)
224b6108616SRui Paulo {
225b9b53389SAdrian Chadd IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
226b6108616SRui Paulo }
227b6108616SRui Paulo
228b032f27cSSam Leffler static int
amrr_update(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)229b032f27cSSam Leffler amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
230b032f27cSSam Leffler struct ieee80211_node *ni)
23174828f25SSam Leffler {
232b032f27cSSam Leffler int rix = amn->amn_rix;
233f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL;
23474828f25SSam Leffler
235b032f27cSSam Leffler KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
236b032f27cSSam Leffler
237f8bf74f2SAdrian Chadd /* 11n or not? Pick the right rateset */
238*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
239f8bf74f2SAdrian Chadd /* XXX ew */
240f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
241f8bf74f2SAdrian Chadd } else {
242f8bf74f2SAdrian Chadd rs = &ni->ni_rates;
243f8bf74f2SAdrian Chadd }
244f8bf74f2SAdrian Chadd
245eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
246eec88845SAdrian Chadd /* XXX TODO: non-11n rate should be divided by two.. */
247f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
248f8bf74f2SAdrian Chadd "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
249f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL,
250f8bf74f2SAdrian Chadd amn->amn_txcnt,
251f8bf74f2SAdrian Chadd amn->amn_retrycnt);
252f8bf74f2SAdrian Chadd
253f8d390beSAdrian Chadd /*
254f8d390beSAdrian Chadd * XXX This is totally bogus for 11n, as although high MCS
255f8d390beSAdrian Chadd * rates for each stream may be failing, the next stream
256f8d390beSAdrian Chadd * should be checked.
257f8d390beSAdrian Chadd *
258f8d390beSAdrian Chadd * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
259f8d390beSAdrian Chadd * MCS23, we should skip 6/7 and try 8 onwards.
260f8d390beSAdrian Chadd */
261b032f27cSSam Leffler if (is_success(amn)) {
26274828f25SSam Leffler amn->amn_success++;
26374828f25SSam Leffler if (amn->amn_success >= amn->amn_success_threshold &&
264f8bf74f2SAdrian Chadd rix + 1 < rs->rs_nrates) {
26574828f25SSam Leffler amn->amn_recovery = 1;
26674828f25SSam Leffler amn->amn_success = 0;
267b032f27cSSam Leffler rix++;
268eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
269eec88845SAdrian Chadd /* XXX TODO: non-11n rate should be divided by two.. */
270b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
271b032f27cSSam Leffler "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
272f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL,
27374828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt);
27474828f25SSam Leffler } else {
27574828f25SSam Leffler amn->amn_recovery = 0;
27674828f25SSam Leffler }
27774828f25SSam Leffler } else if (is_failure(amn)) {
27874828f25SSam Leffler amn->amn_success = 0;
279b032f27cSSam Leffler if (rix > 0) {
28074828f25SSam Leffler if (amn->amn_recovery) {
28174828f25SSam Leffler amn->amn_success_threshold *= 2;
28274828f25SSam Leffler if (amn->amn_success_threshold >
28374828f25SSam Leffler amrr->amrr_max_success_threshold)
28474828f25SSam Leffler amn->amn_success_threshold =
28574828f25SSam Leffler amrr->amrr_max_success_threshold;
28674828f25SSam Leffler } else {
28774828f25SSam Leffler amn->amn_success_threshold =
28874828f25SSam Leffler amrr->amrr_min_success_threshold;
28974828f25SSam Leffler }
290b032f27cSSam Leffler rix--;
291eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
292eec88845SAdrian Chadd /* XXX TODO: non-11n rate should be divided by two.. */
293b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
294b032f27cSSam Leffler "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
295f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL,
29674828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt);
29774828f25SSam Leffler }
29874828f25SSam Leffler amn->amn_recovery = 0;
29974828f25SSam Leffler }
30074828f25SSam Leffler
301b032f27cSSam Leffler /* reset counters */
302b032f27cSSam Leffler amn->amn_txcnt = 0;
303b032f27cSSam Leffler amn->amn_retrycnt = 0;
304b032f27cSSam Leffler
305b032f27cSSam Leffler return rix;
306b032f27cSSam Leffler }
307b032f27cSSam Leffler
308b032f27cSSam Leffler /*
309b032f27cSSam Leffler * Return the rate index to use in sending a data frame.
310b032f27cSSam Leffler * Update our internal state if it's been long enough.
311b032f27cSSam Leffler * If the rate changes we also update ni_txrate to match.
312b032f27cSSam Leffler */
313b6108616SRui Paulo static int
amrr_rate(struct ieee80211_node * ni,void * arg __unused,uint32_t iarg __unused)314b6108616SRui Paulo amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
315b032f27cSSam Leffler {
316b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls;
3174367c2d1SAndriy Voskoboinyk struct ieee80211_amrr *amrr;
318f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL;
319b032f27cSSam Leffler int rix;
320b032f27cSSam Leffler
3214367c2d1SAndriy Voskoboinyk /* XXX should return -1 here, but drivers may not expect this... */
3224367c2d1SAndriy Voskoboinyk if (!amn)
3234367c2d1SAndriy Voskoboinyk {
3244367c2d1SAndriy Voskoboinyk ni->ni_txrate = ni->ni_rates.rs_rates[0];
3254367c2d1SAndriy Voskoboinyk return 0;
3264367c2d1SAndriy Voskoboinyk }
3274367c2d1SAndriy Voskoboinyk
3284367c2d1SAndriy Voskoboinyk amrr = amn->amn_amrr;
3294367c2d1SAndriy Voskoboinyk
330f8bf74f2SAdrian Chadd /* 11n or not? Pick the right rateset */
331*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
332f8bf74f2SAdrian Chadd /* XXX ew */
333f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
334f8bf74f2SAdrian Chadd } else {
335f8bf74f2SAdrian Chadd rs = &ni->ni_rates;
336f8bf74f2SAdrian Chadd }
337f8bf74f2SAdrian Chadd
338b032f27cSSam Leffler if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
339b032f27cSSam Leffler rix = amrr_update(amrr, amn, ni);
340b032f27cSSam Leffler if (rix != amn->amn_rix) {
341b032f27cSSam Leffler /* update public rate */
342f8bf74f2SAdrian Chadd ni->ni_txrate = rs->rs_rates[rix];
343f8bf74f2SAdrian Chadd /* XXX strip basic rate flag from txrate, if non-11n */
344*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni))
345f8bf74f2SAdrian Chadd ni->ni_txrate |= IEEE80211_RATE_MCS;
346f8bf74f2SAdrian Chadd else
347f8bf74f2SAdrian Chadd ni->ni_txrate &= IEEE80211_RATE_VAL;
348b032f27cSSam Leffler amn->amn_rix = rix;
349b032f27cSSam Leffler }
350b032f27cSSam Leffler amn->amn_ticks = ticks;
351b032f27cSSam Leffler } else
352b032f27cSSam Leffler rix = amn->amn_rix;
353b032f27cSSam Leffler return rix;
354b032f27cSSam Leffler }
355b032f27cSSam Leffler
356b6108616SRui Paulo /*
357b6108616SRui Paulo * Update statistics with tx complete status. Ok is non-zero
358b6108616SRui Paulo * if the packet is known to be ACK'd. Retries has the number
359b6108616SRui Paulo * retransmissions (i.e. xmit attempts - 1).
360b6108616SRui Paulo */
361b6108616SRui Paulo static void
amrr_tx_complete(const struct ieee80211_node * ni,const struct ieee80211_ratectl_tx_status * status)362f6930becSAndriy Voskoboinyk amrr_tx_complete(const struct ieee80211_node *ni,
363f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *status)
364b6108616SRui Paulo {
365b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls;
366f6930becSAndriy Voskoboinyk int retries;
367f6930becSAndriy Voskoboinyk
3684367c2d1SAndriy Voskoboinyk if (!amn)
3694367c2d1SAndriy Voskoboinyk return;
3704367c2d1SAndriy Voskoboinyk
371f6930becSAndriy Voskoboinyk retries = 0;
372f6930becSAndriy Voskoboinyk if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
373f6930becSAndriy Voskoboinyk retries = status->long_retries;
374b6108616SRui Paulo
375b6108616SRui Paulo amn->amn_txcnt++;
376f6930becSAndriy Voskoboinyk if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
377b6108616SRui Paulo amn->amn_success++;
378b6108616SRui Paulo amn->amn_retrycnt += retries;
379b6108616SRui Paulo }
380b6108616SRui Paulo
381f6930becSAndriy Voskoboinyk static void
amrr_tx_update_cb(void * arg,struct ieee80211_node * ni)382f6930becSAndriy Voskoboinyk amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
383f6930becSAndriy Voskoboinyk {
384f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats = arg;
385f6930becSAndriy Voskoboinyk struct ieee80211_amrr_node *amn = ni->ni_rctls;
386f6930becSAndriy Voskoboinyk int txcnt, success, retrycnt;
387f6930becSAndriy Voskoboinyk
3884367c2d1SAndriy Voskoboinyk if (!amn)
3894367c2d1SAndriy Voskoboinyk return;
3904367c2d1SAndriy Voskoboinyk
391f6930becSAndriy Voskoboinyk txcnt = stats->nframes;
392f6930becSAndriy Voskoboinyk success = stats->nsuccess;
393f6930becSAndriy Voskoboinyk retrycnt = 0;
394f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
395f6930becSAndriy Voskoboinyk retrycnt = stats->nretries;
396f6930becSAndriy Voskoboinyk
397f6930becSAndriy Voskoboinyk amn->amn_txcnt += txcnt;
398f6930becSAndriy Voskoboinyk amn->amn_success += success;
399f6930becSAndriy Voskoboinyk amn->amn_retrycnt += retrycnt;
400f6930becSAndriy Voskoboinyk }
401f6930becSAndriy Voskoboinyk
402b6108616SRui Paulo /*
403b6108616SRui Paulo * Set tx count/retry statistics explicitly. Intended for
404b6108616SRui Paulo * drivers that poll the device for statistics maintained
405b6108616SRui Paulo * in the device.
406b6108616SRui Paulo */
407b6108616SRui Paulo static void
amrr_tx_update(struct ieee80211vap * vap,struct ieee80211_ratectl_tx_stats * stats)408f6930becSAndriy Voskoboinyk amrr_tx_update(struct ieee80211vap *vap,
409f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats)
410b6108616SRui Paulo {
411b6108616SRui Paulo
412f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
413f6930becSAndriy Voskoboinyk amrr_tx_update_cb(stats, stats->ni);
414f6930becSAndriy Voskoboinyk else {
415f6930becSAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
416f6930becSAndriy Voskoboinyk amrr_tx_update_cb, stats);
417f6930becSAndriy Voskoboinyk }
418b6108616SRui Paulo }
419b6108616SRui Paulo
420b032f27cSSam Leffler static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)421b032f27cSSam Leffler amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
422b032f27cSSam Leffler {
423b6108616SRui Paulo struct ieee80211vap *vap = arg1;
424b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
4254367c2d1SAndriy Voskoboinyk int msecs, error;
426b032f27cSSam Leffler
4274367c2d1SAndriy Voskoboinyk if (!amrr)
4284367c2d1SAndriy Voskoboinyk return ENOMEM;
4294367c2d1SAndriy Voskoboinyk
4304367c2d1SAndriy Voskoboinyk msecs = ticks_to_msecs(amrr->amrr_interval);
431b032f27cSSam Leffler error = sysctl_handle_int(oidp, &msecs, 0, req);
432b032f27cSSam Leffler if (error || !req->newptr)
433b032f27cSSam Leffler return error;
434b6108616SRui Paulo amrr_setinterval(vap, msecs);
435b032f27cSSam Leffler return 0;
436b032f27cSSam Leffler }
437b032f27cSSam Leffler
438b032f27cSSam Leffler static void
amrr_sysctlattach(struct ieee80211vap * vap,struct sysctl_ctx_list * ctx,struct sysctl_oid * tree)439b6108616SRui Paulo amrr_sysctlattach(struct ieee80211vap *vap,
440b032f27cSSam Leffler struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
441b032f27cSSam Leffler {
442b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
443b032f27cSSam Leffler
4444367c2d1SAndriy Voskoboinyk if (!amrr)
4454367c2d1SAndriy Voskoboinyk return;
4464367c2d1SAndriy Voskoboinyk
447b032f27cSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
44808f5e6bbSPawel Biernacki "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
44908f5e6bbSPawel Biernacki vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
450b032f27cSSam Leffler /* XXX bounds check values */
451f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
452b032f27cSSam Leffler "amrr_max_sucess_threshold", CTLFLAG_RW,
453b032f27cSSam Leffler &amrr->amrr_max_success_threshold, 0, "");
454f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
455b032f27cSSam Leffler "amrr_min_sucess_threshold", CTLFLAG_RW,
456b032f27cSSam Leffler &amrr->amrr_min_success_threshold, 0, "");
45774828f25SSam Leffler }
45873931706SAdrian Chadd
45973931706SAdrian Chadd static void
amrr_print_node_rate(struct ieee80211_amrr_node * amn,struct ieee80211_node * ni,struct sbuf * s)460c1475a19SConrad Meyer amrr_print_node_rate(struct ieee80211_amrr_node *amn,
461c1475a19SConrad Meyer struct ieee80211_node *ni, struct sbuf *s)
46273931706SAdrian Chadd {
46373931706SAdrian Chadd int rate;
46473931706SAdrian Chadd struct ieee80211_rateset *rs;
46573931706SAdrian Chadd
466*2014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
46773931706SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
46873931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
46973931706SAdrian Chadd sbuf_printf(s, "rate: MCS %d\n", rate);
47073931706SAdrian Chadd } else {
47173931706SAdrian Chadd rs = &ni->ni_rates;
47273931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
47373931706SAdrian Chadd sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
47473931706SAdrian Chadd }
475c1475a19SConrad Meyer }
47673931706SAdrian Chadd
477c1475a19SConrad Meyer static void
amrr_node_stats(struct ieee80211_node * ni,struct sbuf * s)478c1475a19SConrad Meyer amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
479c1475a19SConrad Meyer {
480c1475a19SConrad Meyer struct ieee80211_amrr_node *amn = ni->ni_rctls;
481c1475a19SConrad Meyer
482c1475a19SConrad Meyer /* XXX TODO: check locking? */
483c1475a19SConrad Meyer
484c1475a19SConrad Meyer if (!amn)
485c1475a19SConrad Meyer return;
486c1475a19SConrad Meyer
487c1475a19SConrad Meyer amrr_print_node_rate(amn, ni, s);
48873931706SAdrian Chadd sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
48973931706SAdrian Chadd sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
49073931706SAdrian Chadd sbuf_printf(s, "success: %u\n", amn->amn_success);
49173931706SAdrian Chadd sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
49273931706SAdrian Chadd sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
49373931706SAdrian Chadd sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
49473931706SAdrian Chadd }
495