xref: /freebsd/sys/net80211/ieee80211_amrr.c (revision 2014462da5b3e630cdbf687d918bbfc0c3a344a4)
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