163a787e0SRui Paulo /*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
263a787e0SRui Paulo 
363a787e0SRui Paulo /*-
44028af95SRui Paulo  * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
563a787e0SRui Paulo  * Copyright (c) 2006
663a787e0SRui Paulo  *	Damien Bergamini <damien.bergamini@free.fr>
763a787e0SRui Paulo  *
863a787e0SRui Paulo  * Permission to use, copy, modify, and distribute this software for any
963a787e0SRui Paulo  * purpose with or without fee is hereby granted, provided that the above
1063a787e0SRui Paulo  * copyright notice and this permission notice appear in all copies.
1163a787e0SRui Paulo  *
1263a787e0SRui Paulo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1363a787e0SRui Paulo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1463a787e0SRui Paulo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1563a787e0SRui Paulo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1663a787e0SRui Paulo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1763a787e0SRui Paulo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1863a787e0SRui Paulo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1963a787e0SRui Paulo  */
2063a787e0SRui Paulo 
21085ff963SMatthew Dillon #include <sys/cdefs.h>
22085ff963SMatthew Dillon __FBSDID("$FreeBSD$");
234028af95SRui Paulo 
2463a787e0SRui Paulo /*-
2563a787e0SRui Paulo  * Naive implementation of the Adaptive Multi Rate Retry algorithm:
2663a787e0SRui Paulo  *
2763a787e0SRui Paulo  * "IEEE 802.11 Rate Adaptation: A Practical Approach"
2863a787e0SRui Paulo  *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
2963a787e0SRui Paulo  *  INRIA Sophia - Projet Planete
3063a787e0SRui Paulo  *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
3163a787e0SRui Paulo  */
3263a787e0SRui Paulo #include "opt_wlan.h"
3363a787e0SRui Paulo 
3463a787e0SRui Paulo #include <sys/param.h>
3563a787e0SRui Paulo #include <sys/kernel.h>
36*4f655ef5SMatthew Dillon #include <sys/malloc.h>
3763a787e0SRui Paulo #include <sys/module.h>
38*4f655ef5SMatthew Dillon #include <sys/sbuf.h>
3963a787e0SRui Paulo #include <sys/socket.h>
4063a787e0SRui Paulo #include <sys/sysctl.h>
4163a787e0SRui Paulo 
4263a787e0SRui Paulo #include <net/if.h>
43085ff963SMatthew Dillon #include <net/if_var.h>
4463a787e0SRui Paulo #include <net/if_media.h>
45085ff963SMatthew Dillon #include <net/ethernet.h>
4663a787e0SRui Paulo 
4763a787e0SRui Paulo #include <netproto/802_11/ieee80211_var.h>
48085ff963SMatthew Dillon #include <netproto/802_11/ieee80211_ht.h>
4963a787e0SRui Paulo #include <netproto/802_11/ieee80211_amrr.h>
504028af95SRui Paulo #include <netproto/802_11/ieee80211_ratectl.h>
5163a787e0SRui Paulo 
5263a787e0SRui Paulo #define is_success(amn)	\
5363a787e0SRui Paulo 	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
5463a787e0SRui Paulo #define is_failure(amn)	\
5563a787e0SRui Paulo 	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
5663a787e0SRui Paulo #define is_enough(amn)		\
5763a787e0SRui Paulo 	((amn)->amn_txcnt > 10)
5863a787e0SRui Paulo 
594028af95SRui Paulo static void	amrr_setinterval(const struct ieee80211vap *, int);
604028af95SRui Paulo static void	amrr_init(struct ieee80211vap *);
614028af95SRui Paulo static void	amrr_deinit(struct ieee80211vap *);
624028af95SRui Paulo static void	amrr_node_init(struct ieee80211_node *);
634028af95SRui Paulo static void	amrr_node_deinit(struct ieee80211_node *);
644028af95SRui Paulo static int	amrr_update(struct ieee80211_amrr *,
654028af95SRui Paulo     			struct ieee80211_amrr_node *, struct ieee80211_node *);
664028af95SRui Paulo static int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
674028af95SRui Paulo static void	amrr_tx_complete(const struct ieee80211vap *,
684028af95SRui Paulo     			const struct ieee80211_node *, int,
694028af95SRui Paulo 			void *, void *);
704028af95SRui Paulo static void	amrr_tx_update(const struct ieee80211vap *vap,
714028af95SRui Paulo 			const struct ieee80211_node *, void *, void *, void *);
724028af95SRui Paulo static void	amrr_sysctlattach(struct ieee80211vap *,
734028af95SRui Paulo 			struct sysctl_ctx_list *, struct sysctl_oid *);
74*4f655ef5SMatthew Dillon static void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
7563a787e0SRui Paulo 
7663a787e0SRui Paulo /* number of references from net80211 layer */
7763a787e0SRui Paulo static	int nrefs = 0;
7863a787e0SRui Paulo 
794028af95SRui Paulo static const struct ieee80211_ratectl amrr = {
804028af95SRui Paulo 	.ir_name	= "amrr",
814028af95SRui Paulo 	.ir_attach	= NULL,
824028af95SRui Paulo 	.ir_detach	= NULL,
834028af95SRui Paulo 	.ir_init	= amrr_init,
844028af95SRui Paulo 	.ir_deinit	= amrr_deinit,
854028af95SRui Paulo 	.ir_node_init	= amrr_node_init,
864028af95SRui Paulo 	.ir_node_deinit	= amrr_node_deinit,
874028af95SRui Paulo 	.ir_rate	= amrr_rate,
884028af95SRui Paulo 	.ir_tx_complete	= amrr_tx_complete,
894028af95SRui Paulo 	.ir_tx_update	= amrr_tx_update,
904028af95SRui Paulo 	.ir_setinterval	= amrr_setinterval,
91*4f655ef5SMatthew Dillon 	.ir_node_stats  = amrr_node_stats,
924028af95SRui Paulo };
934028af95SRui Paulo IEEE80211_RATECTL_MODULE(amrr, 1);
944028af95SRui Paulo IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
954028af95SRui Paulo 
964028af95SRui Paulo static void
amrr_setinterval(const struct ieee80211vap * vap,int msecs)974028af95SRui Paulo amrr_setinterval(const struct ieee80211vap *vap, int msecs)
9863a787e0SRui Paulo {
994028af95SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
10063a787e0SRui Paulo 	int t;
10163a787e0SRui Paulo 
10263a787e0SRui Paulo 	if (msecs < 100)
10363a787e0SRui Paulo 		msecs = 100;
10463a787e0SRui Paulo 	t = msecs_to_ticks(msecs);
10563a787e0SRui Paulo 	amrr->amrr_interval = (t < 1) ? 1 : t;
10663a787e0SRui Paulo }
10763a787e0SRui Paulo 
1084028af95SRui Paulo static void
amrr_init(struct ieee80211vap * vap)1094028af95SRui Paulo amrr_init(struct ieee80211vap *vap)
11063a787e0SRui Paulo {
1114028af95SRui Paulo 	struct ieee80211_amrr *amrr;
11263a787e0SRui Paulo 
1134028af95SRui Paulo 	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
1144028af95SRui Paulo 
115*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
116085ff963SMatthew Dillon 	amrr = vap->iv_rs = kmalloc(sizeof(struct ieee80211_amrr),
117085ff963SMatthew Dillon 	    M_80211_RATECTL, M_INTWAIT|M_ZERO);
118*4f655ef5SMatthew Dillon #else
119*4f655ef5SMatthew Dillon 	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
120*4f655ef5SMatthew Dillon 	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
121*4f655ef5SMatthew Dillon #endif
122085ff963SMatthew Dillon 	if (amrr == NULL) {
123085ff963SMatthew Dillon 		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
124085ff963SMatthew Dillon 		return;
125085ff963SMatthew Dillon 	}
1264028af95SRui Paulo 	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
1274028af95SRui Paulo 	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
1284028af95SRui Paulo 	amrr_setinterval(vap, 500 /* ms */);
1294028af95SRui Paulo 	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
13063a787e0SRui Paulo }
13163a787e0SRui Paulo 
1324028af95SRui Paulo static void
amrr_deinit(struct ieee80211vap * vap)1334028af95SRui Paulo amrr_deinit(struct ieee80211vap *vap)
13463a787e0SRui Paulo {
135*4f655ef5SMatthew Dillon 	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
13663a787e0SRui Paulo }
13763a787e0SRui Paulo 
138085ff963SMatthew Dillon /*
139085ff963SMatthew Dillon  * Return whether 11n rates are possible.
140085ff963SMatthew Dillon  *
141085ff963SMatthew Dillon  * Some 11n devices may return HT information but no HT rates.
142085ff963SMatthew Dillon  * Thus, we shouldn't treat them as an 11n node.
143085ff963SMatthew Dillon  */
144085ff963SMatthew Dillon static int
amrr_node_is_11n(struct ieee80211_node * ni)145085ff963SMatthew Dillon amrr_node_is_11n(struct ieee80211_node *ni)
146085ff963SMatthew Dillon {
147085ff963SMatthew Dillon 
148085ff963SMatthew Dillon 	if (ni->ni_chan == NULL)
149085ff963SMatthew Dillon 		return (0);
150085ff963SMatthew Dillon 	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
151085ff963SMatthew Dillon 		return (0);
152085ff963SMatthew Dillon 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
153085ff963SMatthew Dillon 		return (0);
154085ff963SMatthew Dillon 	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
155085ff963SMatthew Dillon }
156085ff963SMatthew Dillon 
1574028af95SRui Paulo static void
amrr_node_init(struct ieee80211_node * ni)1584028af95SRui Paulo amrr_node_init(struct ieee80211_node *ni)
15963a787e0SRui Paulo {
160085ff963SMatthew Dillon 	const struct ieee80211_rateset *rs = NULL;
1614028af95SRui Paulo 	struct ieee80211vap *vap = ni->ni_vap;
1624028af95SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
1634028af95SRui Paulo 	struct ieee80211_amrr_node *amn;
164085ff963SMatthew Dillon 	uint8_t rate;
16563a787e0SRui Paulo 
166a72c14d0SSascha Wildner 	if (ni->ni_rctls == NULL) {
167*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
168085ff963SMatthew Dillon 		ni->ni_rctls = amn = kmalloc(sizeof(struct ieee80211_amrr_node),
169085ff963SMatthew Dillon 		    M_80211_RATECTL, M_INTWAIT|M_ZERO);
170*4f655ef5SMatthew Dillon #else
171*4f655ef5SMatthew Dillon 		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
172*4f655ef5SMatthew Dillon 		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
173*4f655ef5SMatthew Dillon #endif
174085ff963SMatthew Dillon 		if (amn == NULL) {
175085ff963SMatthew Dillon 			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
176085ff963SMatthew Dillon 			    "structure\n");
177085ff963SMatthew Dillon 			return;
178a72c14d0SSascha Wildner 		}
179085ff963SMatthew Dillon 	} else
180085ff963SMatthew Dillon 		amn = ni->ni_rctls;
18163a787e0SRui Paulo 	amn->amn_amrr = amrr;
18263a787e0SRui Paulo 	amn->amn_success = 0;
18363a787e0SRui Paulo 	amn->amn_recovery = 0;
18463a787e0SRui Paulo 	amn->amn_txcnt = amn->amn_retrycnt = 0;
18563a787e0SRui Paulo 	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
18663a787e0SRui Paulo 
187085ff963SMatthew Dillon 	/* 11n or not? Pick the right rateset */
188085ff963SMatthew Dillon 	if (amrr_node_is_11n(ni)) {
189085ff963SMatthew Dillon 		/* XXX ew */
190085ff963SMatthew Dillon 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
191085ff963SMatthew Dillon 		    "%s: 11n node", __func__);
192085ff963SMatthew Dillon 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
193085ff963SMatthew Dillon 	} else {
194085ff963SMatthew Dillon 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
195085ff963SMatthew Dillon 		    "%s: non-11n node", __func__);
196085ff963SMatthew Dillon 		rs = &ni->ni_rates;
197085ff963SMatthew Dillon 	}
198085ff963SMatthew Dillon 
199085ff963SMatthew Dillon 	/* Initial rate - lowest */
200085ff963SMatthew Dillon 	rate = rs->rs_rates[0];
201085ff963SMatthew Dillon 
202085ff963SMatthew Dillon 	/* XXX clear the basic rate flag if it's not 11n */
203085ff963SMatthew Dillon 	if (! amrr_node_is_11n(ni))
204085ff963SMatthew Dillon 		rate &= IEEE80211_RATE_VAL;
205085ff963SMatthew Dillon 
206085ff963SMatthew Dillon 	/* pick initial rate from the rateset - HT or otherwise */
207085ff963SMatthew Dillon 	/* Pick something low that's likely to succeed */
208085ff963SMatthew Dillon 	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
209085ff963SMatthew Dillon 	    amn->amn_rix--) {
210085ff963SMatthew Dillon 		/* legacy - anything < 36mbit, stop searching */
211085ff963SMatthew Dillon 		/* 11n - stop at MCS4 */
212085ff963SMatthew Dillon 		if (amrr_node_is_11n(ni)) {
213085ff963SMatthew Dillon 			if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
214085ff963SMatthew Dillon 				break;
215085ff963SMatthew Dillon 		} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
216085ff963SMatthew Dillon 			break;
217085ff963SMatthew Dillon 	}
218085ff963SMatthew Dillon 	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
219085ff963SMatthew Dillon 
220085ff963SMatthew Dillon 	/* if the rate is an 11n rate, ensure the MCS bit is set */
221085ff963SMatthew Dillon 	if (amrr_node_is_11n(ni))
222085ff963SMatthew Dillon 		rate |= IEEE80211_RATE_MCS;
223085ff963SMatthew Dillon 
224085ff963SMatthew Dillon 	/* Assign initial rate from the rateset */
225085ff963SMatthew Dillon 	ni->ni_txrate = rate;
22663a787e0SRui Paulo 	amn->amn_ticks = ticks;
22763a787e0SRui Paulo 
22863a787e0SRui Paulo 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
229085ff963SMatthew Dillon 	    "AMRR: nrates=%d, initial rate %d",
230085ff963SMatthew Dillon 	    rs->rs_nrates,
231085ff963SMatthew Dillon 	    rate);
23263a787e0SRui Paulo }
23363a787e0SRui Paulo 
2344028af95SRui Paulo static void
amrr_node_deinit(struct ieee80211_node * ni)2354028af95SRui Paulo amrr_node_deinit(struct ieee80211_node *ni)
2364028af95SRui Paulo {
237*4f655ef5SMatthew Dillon 	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
2384028af95SRui Paulo }
2394028af95SRui Paulo 
24063a787e0SRui Paulo static int
amrr_update(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)24163a787e0SRui Paulo amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
24263a787e0SRui Paulo     struct ieee80211_node *ni)
24363a787e0SRui Paulo {
24463a787e0SRui Paulo 	int rix = amn->amn_rix;
245085ff963SMatthew Dillon 	const struct ieee80211_rateset *rs = NULL;
24663a787e0SRui Paulo 
24763a787e0SRui Paulo 	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
24863a787e0SRui Paulo 
249085ff963SMatthew Dillon 	/* 11n or not? Pick the right rateset */
250085ff963SMatthew Dillon 	if (amrr_node_is_11n(ni)) {
251085ff963SMatthew Dillon 		/* XXX ew */
252085ff963SMatthew Dillon 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
253085ff963SMatthew Dillon 	} else {
254085ff963SMatthew Dillon 		rs = &ni->ni_rates;
255085ff963SMatthew Dillon 	}
256085ff963SMatthew Dillon 
257085ff963SMatthew Dillon 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
258085ff963SMatthew Dillon 	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
259085ff963SMatthew Dillon 	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
260085ff963SMatthew Dillon 	    amn->amn_txcnt,
261085ff963SMatthew Dillon 	    amn->amn_retrycnt);
262085ff963SMatthew Dillon 
263085ff963SMatthew Dillon 	/*
264085ff963SMatthew Dillon 	 * XXX This is totally bogus for 11n, as although high MCS
265085ff963SMatthew Dillon 	 * rates for each stream may be failing, the next stream
266085ff963SMatthew Dillon 	 * should be checked.
267085ff963SMatthew Dillon 	 *
268085ff963SMatthew Dillon 	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
269085ff963SMatthew Dillon 	 * MCS23, we should skip 6/7 and try 8 onwards.
270085ff963SMatthew Dillon 	 */
27163a787e0SRui Paulo 	if (is_success(amn)) {
27263a787e0SRui Paulo 		amn->amn_success++;
27363a787e0SRui Paulo 		if (amn->amn_success >= amn->amn_success_threshold &&
274085ff963SMatthew Dillon 		    rix + 1 < rs->rs_nrates) {
27563a787e0SRui Paulo 			amn->amn_recovery = 1;
27663a787e0SRui Paulo 			amn->amn_success = 0;
27763a787e0SRui Paulo 			rix++;
27863a787e0SRui Paulo 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
27963a787e0SRui Paulo 			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
280085ff963SMatthew Dillon 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
28163a787e0SRui Paulo 			    amn->amn_txcnt, amn->amn_retrycnt);
28263a787e0SRui Paulo 		} else {
28363a787e0SRui Paulo 			amn->amn_recovery = 0;
28463a787e0SRui Paulo 		}
28563a787e0SRui Paulo 	} else if (is_failure(amn)) {
28663a787e0SRui Paulo 		amn->amn_success = 0;
28763a787e0SRui Paulo 		if (rix > 0) {
28863a787e0SRui Paulo 			if (amn->amn_recovery) {
28963a787e0SRui Paulo 				amn->amn_success_threshold *= 2;
29063a787e0SRui Paulo 				if (amn->amn_success_threshold >
29163a787e0SRui Paulo 				    amrr->amrr_max_success_threshold)
29263a787e0SRui Paulo 					amn->amn_success_threshold =
29363a787e0SRui Paulo 					    amrr->amrr_max_success_threshold;
29463a787e0SRui Paulo 			} else {
29563a787e0SRui Paulo 				amn->amn_success_threshold =
29663a787e0SRui Paulo 				    amrr->amrr_min_success_threshold;
29763a787e0SRui Paulo 			}
29863a787e0SRui Paulo 			rix--;
29963a787e0SRui Paulo 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
30063a787e0SRui Paulo 			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
301085ff963SMatthew Dillon 			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
30263a787e0SRui Paulo 			    amn->amn_txcnt, amn->amn_retrycnt);
30363a787e0SRui Paulo 		}
30463a787e0SRui Paulo 		amn->amn_recovery = 0;
30563a787e0SRui Paulo 	}
30663a787e0SRui Paulo 
30763a787e0SRui Paulo 	/* reset counters */
30863a787e0SRui Paulo 	amn->amn_txcnt = 0;
30963a787e0SRui Paulo 	amn->amn_retrycnt = 0;
31063a787e0SRui Paulo 
31163a787e0SRui Paulo 	return rix;
31263a787e0SRui Paulo }
31363a787e0SRui Paulo 
31463a787e0SRui Paulo /*
31563a787e0SRui Paulo  * Return the rate index to use in sending a data frame.
31663a787e0SRui Paulo  * Update our internal state if it's been long enough.
31763a787e0SRui Paulo  * If the rate changes we also update ni_txrate to match.
31863a787e0SRui Paulo  */
3194028af95SRui Paulo static int
amrr_rate(struct ieee80211_node * ni,void * arg __unused,uint32_t iarg __unused)3204028af95SRui Paulo amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
32163a787e0SRui Paulo {
3224028af95SRui Paulo 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
32363a787e0SRui Paulo 	struct ieee80211_amrr *amrr = amn->amn_amrr;
324085ff963SMatthew Dillon 	const struct ieee80211_rateset *rs = NULL;
32563a787e0SRui Paulo 	int rix;
32663a787e0SRui Paulo 
327085ff963SMatthew Dillon 	/* 11n or not? Pick the right rateset */
328085ff963SMatthew Dillon 	if (amrr_node_is_11n(ni)) {
329085ff963SMatthew Dillon 		/* XXX ew */
330085ff963SMatthew Dillon 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
331085ff963SMatthew Dillon 	} else {
332085ff963SMatthew Dillon 		rs = &ni->ni_rates;
333085ff963SMatthew Dillon 	}
334085ff963SMatthew Dillon 
33563a787e0SRui Paulo 	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
33663a787e0SRui Paulo 		rix = amrr_update(amrr, amn, ni);
33763a787e0SRui Paulo 		if (rix != amn->amn_rix) {
33863a787e0SRui Paulo 			/* update public rate */
339085ff963SMatthew Dillon 			ni->ni_txrate = rs->rs_rates[rix];
340085ff963SMatthew Dillon 			/* XXX strip basic rate flag from txrate, if non-11n */
341085ff963SMatthew Dillon 			if (amrr_node_is_11n(ni))
342085ff963SMatthew Dillon 				ni->ni_txrate |= IEEE80211_RATE_MCS;
343085ff963SMatthew Dillon 			else
344085ff963SMatthew Dillon 				ni->ni_txrate &= IEEE80211_RATE_VAL;
34563a787e0SRui Paulo 			amn->amn_rix = rix;
34663a787e0SRui Paulo 		}
34763a787e0SRui Paulo 		amn->amn_ticks = ticks;
34863a787e0SRui Paulo 	} else
34963a787e0SRui Paulo 		rix = amn->amn_rix;
35063a787e0SRui Paulo 	return rix;
35163a787e0SRui Paulo }
35263a787e0SRui Paulo 
3534028af95SRui Paulo /*
3544028af95SRui Paulo  * Update statistics with tx complete status.  Ok is non-zero
3554028af95SRui Paulo  * if the packet is known to be ACK'd.  Retries has the number
3564028af95SRui Paulo  * retransmissions (i.e. xmit attempts - 1).
3574028af95SRui Paulo  */
3584028af95SRui Paulo static void
amrr_tx_complete(const struct ieee80211vap * vap,const struct ieee80211_node * ni,int ok,void * arg1,void * arg2 __unused)3594028af95SRui Paulo amrr_tx_complete(const struct ieee80211vap *vap,
3604028af95SRui Paulo     const struct ieee80211_node *ni, int ok,
3614028af95SRui Paulo     void *arg1, void *arg2 __unused)
3624028af95SRui Paulo {
3634028af95SRui Paulo 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
3644028af95SRui Paulo 	int retries = *(int *)arg1;
3654028af95SRui Paulo 
3664028af95SRui Paulo 	amn->amn_txcnt++;
3674028af95SRui Paulo 	if (ok)
3684028af95SRui Paulo 		amn->amn_success++;
3694028af95SRui Paulo 	amn->amn_retrycnt += retries;
3704028af95SRui Paulo }
3714028af95SRui Paulo 
3724028af95SRui Paulo /*
3734028af95SRui Paulo  * Set tx count/retry statistics explicitly.  Intended for
3744028af95SRui Paulo  * drivers that poll the device for statistics maintained
3754028af95SRui Paulo  * in the device.
3764028af95SRui Paulo  */
3774028af95SRui Paulo static void
amrr_tx_update(const struct ieee80211vap * vap,const struct ieee80211_node * ni,void * arg1,void * arg2,void * arg3)3784028af95SRui Paulo amrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni,
3794028af95SRui Paulo     void *arg1, void *arg2, void *arg3)
3804028af95SRui Paulo {
3814028af95SRui Paulo 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
3824028af95SRui Paulo 	int txcnt = *(int *)arg1, success = *(int *)arg2, retrycnt = *(int *)arg3;
3834028af95SRui Paulo 
3844028af95SRui Paulo 	amn->amn_txcnt = txcnt;
3854028af95SRui Paulo 	amn->amn_success = success;
3864028af95SRui Paulo 	amn->amn_retrycnt = retrycnt;
3874028af95SRui Paulo }
3884028af95SRui Paulo 
38963a787e0SRui Paulo static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)39063a787e0SRui Paulo amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
39163a787e0SRui Paulo {
3924028af95SRui Paulo 	struct ieee80211vap *vap = arg1;
3934028af95SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
39463a787e0SRui Paulo 	int msecs = ticks_to_msecs(amrr->amrr_interval);
39563a787e0SRui Paulo 	int error;
39663a787e0SRui Paulo 
39763a787e0SRui Paulo 	error = sysctl_handle_int(oidp, &msecs, 0, req);
398085ff963SMatthew Dillon 	if (error || !req->newptr)
39947156d48SMatthew Dillon 		return error;
400085ff963SMatthew Dillon 	amrr_setinterval(vap, msecs);
401085ff963SMatthew Dillon 	return 0;
40263a787e0SRui Paulo }
40363a787e0SRui Paulo 
40463a787e0SRui Paulo static void
amrr_sysctlattach(struct ieee80211vap * vap,struct sysctl_ctx_list * ctx,struct sysctl_oid * tree)4054028af95SRui Paulo amrr_sysctlattach(struct ieee80211vap *vap,
40663a787e0SRui Paulo     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
40763a787e0SRui Paulo {
4084028af95SRui Paulo 	struct ieee80211_amrr *amrr = vap->iv_rs;
40963a787e0SRui Paulo 
41063a787e0SRui Paulo 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
4114028af95SRui Paulo 	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
41263a787e0SRui Paulo 	    0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
41363a787e0SRui Paulo 	/* XXX bounds check values */
414085ff963SMatthew Dillon 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
41563a787e0SRui Paulo 	    "amrr_max_sucess_threshold", CTLFLAG_RW,
41663a787e0SRui Paulo 	    &amrr->amrr_max_success_threshold, 0, "");
417085ff963SMatthew Dillon 	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
41863a787e0SRui Paulo 	    "amrr_min_sucess_threshold", CTLFLAG_RW,
41963a787e0SRui Paulo 	    &amrr->amrr_min_success_threshold, 0, "");
42063a787e0SRui Paulo }
421*4f655ef5SMatthew Dillon 
422*4f655ef5SMatthew Dillon static void
amrr_node_stats(struct ieee80211_node * ni,struct sbuf * s)423*4f655ef5SMatthew Dillon amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
424*4f655ef5SMatthew Dillon {
425*4f655ef5SMatthew Dillon 	int rate;
426*4f655ef5SMatthew Dillon 	struct ieee80211_amrr_node *amn = ni->ni_rctls;
427*4f655ef5SMatthew Dillon 	struct ieee80211_rateset *rs;
428*4f655ef5SMatthew Dillon 
429*4f655ef5SMatthew Dillon 	/* XXX TODO: check locking? */
430*4f655ef5SMatthew Dillon 
431*4f655ef5SMatthew Dillon 	/* XXX TODO: this should be a method */
432*4f655ef5SMatthew Dillon 	if (amrr_node_is_11n(ni)) {
433*4f655ef5SMatthew Dillon 		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
434*4f655ef5SMatthew Dillon 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
435*4f655ef5SMatthew Dillon 		sbuf_printf(s, "rate: MCS %d\n", rate);
436*4f655ef5SMatthew Dillon 	} else {
437*4f655ef5SMatthew Dillon 		rs = &ni->ni_rates;
438*4f655ef5SMatthew Dillon 		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
439*4f655ef5SMatthew Dillon 		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
440*4f655ef5SMatthew Dillon 	}
441*4f655ef5SMatthew Dillon 
442*4f655ef5SMatthew Dillon 	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
443*4f655ef5SMatthew Dillon 	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
444*4f655ef5SMatthew Dillon 	sbuf_printf(s, "success: %u\n", amn->amn_success);
445*4f655ef5SMatthew Dillon 	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
446*4f655ef5SMatthew Dillon 	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
447*4f655ef5SMatthew Dillon 	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
448*4f655ef5SMatthew Dillon }
449