1164633Ssam/*	$OpenBSD: ieee80211_amrr.c,v 1.1 2006/06/17 19:07:19 damien Exp $	*/
2164633Ssam
3164633Ssam/*-
4206358Srpaulo * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
5164633Ssam * Copyright (c) 2006
6164633Ssam *	Damien Bergamini <damien.bergamini@free.fr>
7164633Ssam *
8164633Ssam * Permission to use, copy, modify, and distribute this software for any
9164633Ssam * purpose with or without fee is hereby granted, provided that the above
10164633Ssam * copyright notice and this permission notice appear in all copies.
11164633Ssam *
12164633Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13164633Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14164633Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15164633Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16164633Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17164633Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18164633Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19164633Ssam */
20164633Ssam
21164633Ssam#include <sys/cdefs.h>
22164633Ssam__FBSDID("$FreeBSD: stable/11/sys/net80211/ieee80211_amrr.c 359746 2020-04-09 15:30:21Z eugen $");
23164633Ssam
24164633Ssam/*-
25164633Ssam * Naive implementation of the Adaptive Multi Rate Retry algorithm:
26164633Ssam *
27164633Ssam * "IEEE 802.11 Rate Adaptation: A Practical Approach"
28164633Ssam *  Mathieu Lacage, Hossein Manshaei, Thierry Turletti
29164633Ssam *  INRIA Sophia - Projet Planete
30164633Ssam *  http://www-sop.inria.fr/rapports/sophia/RR-5208.html
31164633Ssam */
32178354Ssam#include "opt_wlan.h"
33178354Ssam
34164633Ssam#include <sys/param.h>
35164633Ssam#include <sys/kernel.h>
36295126Sglebius#include <sys/malloc.h>
37164633Ssam#include <sys/module.h>
38296925Sadrian#include <sys/sbuf.h>
39164633Ssam#include <sys/socket.h>
40164633Ssam#include <sys/sysctl.h>
41164633Ssam
42164633Ssam#include <net/if.h>
43257176Sglebius#include <net/if_var.h>
44164633Ssam#include <net/if_media.h>
45257241Sglebius#include <net/ethernet.h>
46164633Ssam
47164633Ssam#ifdef INET
48164633Ssam#include <netinet/in.h>
49164633Ssam#include <netinet/if_ether.h>
50164633Ssam#endif
51164633Ssam
52164633Ssam#include <net80211/ieee80211_var.h>
53252727Sadrian#include <net80211/ieee80211_ht.h>
54164633Ssam#include <net80211/ieee80211_amrr.h>
55206358Srpaulo#include <net80211/ieee80211_ratectl.h>
56164633Ssam
57164633Ssam#define is_success(amn)	\
58164633Ssam	((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
59164633Ssam#define is_failure(amn)	\
60164633Ssam	((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
61164633Ssam#define is_enough(amn)		\
62164633Ssam	((amn)->amn_txcnt > 10)
63164633Ssam
64206358Srpaulostatic void	amrr_setinterval(const struct ieee80211vap *, int);
65206358Srpaulostatic void	amrr_init(struct ieee80211vap *);
66206358Srpaulostatic void	amrr_deinit(struct ieee80211vap *);
67206358Srpaulostatic void	amrr_node_init(struct ieee80211_node *);
68206358Srpaulostatic void	amrr_node_deinit(struct ieee80211_node *);
69206358Srpaulostatic int	amrr_update(struct ieee80211_amrr *,
70206358Srpaulo    			struct ieee80211_amrr_node *, struct ieee80211_node *);
71206358Srpaulostatic int	amrr_rate(struct ieee80211_node *, void *, uint32_t);
72206358Srpaulostatic void	amrr_tx_complete(const struct ieee80211vap *,
73206358Srpaulo    			const struct ieee80211_node *, int,
74206358Srpaulo			void *, void *);
75206358Srpaulostatic void	amrr_tx_update(const struct ieee80211vap *vap,
76206358Srpaulo			const struct ieee80211_node *, void *, void *, void *);
77206358Srpaulostatic void	amrr_sysctlattach(struct ieee80211vap *,
78206358Srpaulo			struct sysctl_ctx_list *, struct sysctl_oid *);
79296925Sadrianstatic void	amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
80178354Ssam
81178354Ssam/* number of references from net80211 layer */
82178354Ssamstatic	int nrefs = 0;
83178354Ssam
84206358Srpaulostatic const struct ieee80211_ratectl amrr = {
85206358Srpaulo	.ir_name	= "amrr",
86206358Srpaulo	.ir_attach	= NULL,
87206358Srpaulo	.ir_detach	= NULL,
88206358Srpaulo	.ir_init	= amrr_init,
89206358Srpaulo	.ir_deinit	= amrr_deinit,
90206358Srpaulo	.ir_node_init	= amrr_node_init,
91206358Srpaulo	.ir_node_deinit	= amrr_node_deinit,
92206358Srpaulo	.ir_rate	= amrr_rate,
93206358Srpaulo	.ir_tx_complete	= amrr_tx_complete,
94206358Srpaulo	.ir_tx_update	= amrr_tx_update,
95206358Srpaulo	.ir_setinterval	= amrr_setinterval,
96296925Sadrian	.ir_node_stats	= amrr_node_stats,
97206358Srpaulo};
98206358SrpauloIEEE80211_RATECTL_MODULE(amrr, 1);
99206358SrpauloIEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
100206358Srpaulo
101206358Srpaulostatic void
102206358Srpauloamrr_setinterval(const struct ieee80211vap *vap, int msecs)
103178354Ssam{
104206358Srpaulo	struct ieee80211_amrr *amrr = vap->iv_rs;
105178354Ssam	int t;
106178354Ssam
107343035Savos	if (!amrr)
108343035Savos		return;
109343035Savos
110178354Ssam	if (msecs < 100)
111178354Ssam		msecs = 100;
112178354Ssam	t = msecs_to_ticks(msecs);
113178354Ssam	amrr->amrr_interval = (t < 1) ? 1 : t;
114178354Ssam}
115178354Ssam
116206358Srpaulostatic void
117206358Srpauloamrr_init(struct ieee80211vap *vap)
118164633Ssam{
119206358Srpaulo	struct ieee80211_amrr *amrr;
120178354Ssam
121206358Srpaulo	KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
122206358Srpaulo
123321724Savos	nrefs++;		/* XXX locking */
124283538Sadrian	amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
125283538Sadrian	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
126206419Srpaulo	if (amrr == NULL) {
127206419Srpaulo		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
128206419Srpaulo		return;
129206419Srpaulo	}
130206358Srpaulo	amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
131206358Srpaulo	amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
132206358Srpaulo	amrr_setinterval(vap, 500 /* ms */);
133206358Srpaulo	amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
134164633Ssam}
135164633Ssam
136206358Srpaulostatic void
137206358Srpauloamrr_deinit(struct ieee80211vap *vap)
138178354Ssam{
139283538Sadrian	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
140321724Savos	KASSERT(nrefs > 0, ("imbalanced attach/detach"));
141321724Savos	nrefs--;		/* XXX locking */
142178354Ssam}
143178354Ssam
144257412Sadrian/*
145257412Sadrian * Return whether 11n rates are possible.
146257412Sadrian *
147257412Sadrian * Some 11n devices may return HT information but no HT rates.
148257412Sadrian * Thus, we shouldn't treat them as an 11n node.
149257412Sadrian */
150252727Sadrianstatic int
151252727Sadrianamrr_node_is_11n(struct ieee80211_node *ni)
152252727Sadrian{
153252727Sadrian
154252727Sadrian	if (ni->ni_chan == NULL)
155252727Sadrian		return (0);
156252727Sadrian	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
157252727Sadrian		return (0);
158257412Sadrian	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0)
159257412Sadrian		return (0);
160252727Sadrian	return (IEEE80211_IS_CHAN_HT(ni->ni_chan));
161252727Sadrian}
162252727Sadrian
163206358Srpaulostatic void
164206358Srpauloamrr_node_init(struct ieee80211_node *ni)
165164633Ssam{
166252727Sadrian	const struct ieee80211_rateset *rs = NULL;
167206358Srpaulo	struct ieee80211vap *vap = ni->ni_vap;
168206358Srpaulo	struct ieee80211_amrr *amrr = vap->iv_rs;
169206358Srpaulo	struct ieee80211_amrr_node *amn;
170252727Sadrian	uint8_t rate;
171178354Ssam
172343035Savos	if (!amrr) {
173343035Savos		if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
174343035Savos		    "per-node structure allocation skipped\n");
175343035Savos		return;
176343035Savos	}
177343035Savos
178207323Srpaulo	if (ni->ni_rctls == NULL) {
179283538Sadrian		ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
180283538Sadrian		    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
181207323Srpaulo		if (amn == NULL) {
182207323Srpaulo			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
183207323Srpaulo			    "structure\n");
184207323Srpaulo			return;
185207323Srpaulo		}
186207323Srpaulo	} else
187207323Srpaulo		amn = ni->ni_rctls;
188178354Ssam	amn->amn_amrr = amrr;
189164633Ssam	amn->amn_success = 0;
190164633Ssam	amn->amn_recovery = 0;
191164633Ssam	amn->amn_txcnt = amn->amn_retrycnt = 0;
192164633Ssam	amn->amn_success_threshold = amrr->amrr_min_success_threshold;
193178354Ssam
194252727Sadrian	/* 11n or not? Pick the right rateset */
195252727Sadrian	if (amrr_node_is_11n(ni)) {
196252727Sadrian		/* XXX ew */
197252727Sadrian		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
198252727Sadrian		    "%s: 11n node", __func__);
199252727Sadrian		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
200252727Sadrian	} else {
201252727Sadrian		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
202252727Sadrian		    "%s: non-11n node", __func__);
203252727Sadrian		rs = &ni->ni_rates;
204252727Sadrian	}
205252727Sadrian
206252727Sadrian	/* Initial rate - lowest */
207252727Sadrian	rate = rs->rs_rates[0];
208252727Sadrian
209252727Sadrian	/* XXX clear the basic rate flag if it's not 11n */
210252727Sadrian	if (! amrr_node_is_11n(ni))
211252727Sadrian		rate &= IEEE80211_RATE_VAL;
212252727Sadrian
213252727Sadrian	/* pick initial rate from the rateset - HT or otherwise */
214270206Sadrian	/* Pick something low that's likely to succeed */
215252727Sadrian	for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
216252727Sadrian	    amn->amn_rix--) {
217252727Sadrian		/* legacy - anything < 36mbit, stop searching */
218270206Sadrian		/* 11n - stop at MCS4 */
219257881Sadrian		if (amrr_node_is_11n(ni)) {
220270206Sadrian			if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
221257881Sadrian				break;
222257881Sadrian		} else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72)
223252727Sadrian			break;
224252727Sadrian	}
225257881Sadrian	rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
226252727Sadrian
227252727Sadrian	/* if the rate is an 11n rate, ensure the MCS bit is set */
228252727Sadrian	if (amrr_node_is_11n(ni))
229252727Sadrian		rate |= IEEE80211_RATE_MCS;
230252727Sadrian
231252727Sadrian	/* Assign initial rate from the rateset */
232252727Sadrian	ni->ni_txrate = rate;
233178354Ssam	amn->amn_ticks = ticks;
234178354Ssam
235302307Sadrian	/* XXX TODO: we really need a rate-to-string method */
236302307Sadrian	/* XXX TODO: non-11n rate should be divided by two.. */
237178354Ssam	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
238302307Sadrian	    "AMRR: nrates=%d, initial rate %s%d",
239252727Sadrian	    rs->rs_nrates,
240302307Sadrian	    amrr_node_is_11n(ni) ? "MCS " : "",
241302307Sadrian	    rate & IEEE80211_RATE_VAL);
242164633Ssam}
243164633Ssam
244206358Srpaulostatic void
245206358Srpauloamrr_node_deinit(struct ieee80211_node *ni)
246206358Srpaulo{
247283538Sadrian	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
248206358Srpaulo}
249206358Srpaulo
250178354Ssamstatic int
251178354Ssamamrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
252178354Ssam    struct ieee80211_node *ni)
253164633Ssam{
254178354Ssam	int rix = amn->amn_rix;
255252727Sadrian	const struct ieee80211_rateset *rs = NULL;
256164633Ssam
257178354Ssam	KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
258178354Ssam
259252727Sadrian	/* 11n or not? Pick the right rateset */
260252727Sadrian	if (amrr_node_is_11n(ni)) {
261252727Sadrian		/* XXX ew */
262252727Sadrian		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
263252727Sadrian	} else {
264252727Sadrian		rs = &ni->ni_rates;
265252727Sadrian	}
266252727Sadrian
267302307Sadrian	/* XXX TODO: we really need a rate-to-string method */
268302307Sadrian	/* XXX TODO: non-11n rate should be divided by two.. */
269252727Sadrian	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
270252727Sadrian	    "AMRR: current rate %d, txcnt=%d, retrycnt=%d",
271252727Sadrian	    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
272252727Sadrian	    amn->amn_txcnt,
273252727Sadrian	    amn->amn_retrycnt);
274252727Sadrian
275252736Sadrian	/*
276252736Sadrian	 * XXX This is totally bogus for 11n, as although high MCS
277252736Sadrian	 * rates for each stream may be failing, the next stream
278252736Sadrian	 * should be checked.
279252736Sadrian	 *
280252736Sadrian	 * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
281252736Sadrian	 * MCS23, we should skip 6/7 and try 8 onwards.
282252736Sadrian	 */
283178354Ssam	if (is_success(amn)) {
284164633Ssam		amn->amn_success++;
285164633Ssam		if (amn->amn_success >= amn->amn_success_threshold &&
286252727Sadrian		    rix + 1 < rs->rs_nrates) {
287164633Ssam			amn->amn_recovery = 1;
288164633Ssam			amn->amn_success = 0;
289178354Ssam			rix++;
290302307Sadrian			/* XXX TODO: we really need a rate-to-string method */
291302307Sadrian			/* XXX TODO: non-11n rate should be divided by two.. */
292178354Ssam			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
293178354Ssam			    "AMRR increasing rate %d (txcnt=%d retrycnt=%d)",
294252727Sadrian			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
295164633Ssam			    amn->amn_txcnt, amn->amn_retrycnt);
296164633Ssam		} else {
297164633Ssam			amn->amn_recovery = 0;
298164633Ssam		}
299164633Ssam	} else if (is_failure(amn)) {
300164633Ssam		amn->amn_success = 0;
301178354Ssam		if (rix > 0) {
302164633Ssam			if (amn->amn_recovery) {
303164633Ssam				amn->amn_success_threshold *= 2;
304164633Ssam				if (amn->amn_success_threshold >
305164633Ssam				    amrr->amrr_max_success_threshold)
306164633Ssam					amn->amn_success_threshold =
307164633Ssam					    amrr->amrr_max_success_threshold;
308164633Ssam			} else {
309164633Ssam				amn->amn_success_threshold =
310164633Ssam				    amrr->amrr_min_success_threshold;
311164633Ssam			}
312178354Ssam			rix--;
313302307Sadrian			/* XXX TODO: we really need a rate-to-string method */
314302307Sadrian			/* XXX TODO: non-11n rate should be divided by two.. */
315178354Ssam			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
316178354Ssam			    "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)",
317252727Sadrian			    rs->rs_rates[rix] & IEEE80211_RATE_VAL,
318164633Ssam			    amn->amn_txcnt, amn->amn_retrycnt);
319164633Ssam		}
320164633Ssam		amn->amn_recovery = 0;
321164633Ssam	}
322164633Ssam
323178354Ssam	/* reset counters */
324178354Ssam	amn->amn_txcnt = 0;
325178354Ssam	amn->amn_retrycnt = 0;
326178354Ssam
327178354Ssam	return rix;
328164633Ssam}
329164633Ssam
330164633Ssam/*
331178354Ssam * Return the rate index to use in sending a data frame.
332178354Ssam * Update our internal state if it's been long enough.
333178354Ssam * If the rate changes we also update ni_txrate to match.
334164633Ssam */
335206358Srpaulostatic int
336206358Srpauloamrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
337178354Ssam{
338206358Srpaulo	struct ieee80211_amrr_node *amn = ni->ni_rctls;
339343035Savos	struct ieee80211_amrr *amrr;
340252727Sadrian	const struct ieee80211_rateset *rs = NULL;
341178354Ssam	int rix;
342178354Ssam
343343035Savos	/* XXX should return -1 here, but drivers may not expect this... */
344343035Savos	if (!amn)
345343035Savos	{
346343035Savos		ni->ni_txrate = ni->ni_rates.rs_rates[0];
347343035Savos		return 0;
348343035Savos	}
349343035Savos
350343035Savos	amrr = amn->amn_amrr;
351343035Savos
352252727Sadrian	/* 11n or not? Pick the right rateset */
353252727Sadrian	if (amrr_node_is_11n(ni)) {
354252727Sadrian		/* XXX ew */
355252727Sadrian		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
356252727Sadrian	} else {
357252727Sadrian		rs = &ni->ni_rates;
358252727Sadrian	}
359252727Sadrian
360178354Ssam	if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
361178354Ssam		rix = amrr_update(amrr, amn, ni);
362178354Ssam		if (rix != amn->amn_rix) {
363178354Ssam			/* update public rate */
364252727Sadrian			ni->ni_txrate = rs->rs_rates[rix];
365252727Sadrian			/* XXX strip basic rate flag from txrate, if non-11n */
366252727Sadrian			if (amrr_node_is_11n(ni))
367252727Sadrian				ni->ni_txrate |= IEEE80211_RATE_MCS;
368252727Sadrian			else
369252727Sadrian				ni->ni_txrate &= IEEE80211_RATE_VAL;
370178354Ssam			amn->amn_rix = rix;
371178354Ssam		}
372178354Ssam		amn->amn_ticks = ticks;
373178354Ssam	} else
374178354Ssam		rix = amn->amn_rix;
375178354Ssam	return rix;
376178354Ssam}
377178354Ssam
378206358Srpaulo/*
379206358Srpaulo * Update statistics with tx complete status.  Ok is non-zero
380206358Srpaulo * if the packet is known to be ACK'd.  Retries has the number
381206358Srpaulo * retransmissions (i.e. xmit attempts - 1).
382206358Srpaulo */
383206358Srpaulostatic void
384206358Srpauloamrr_tx_complete(const struct ieee80211vap *vap,
385206358Srpaulo    const struct ieee80211_node *ni, int ok,
386206358Srpaulo    void *arg1, void *arg2 __unused)
387206358Srpaulo{
388206358Srpaulo	struct ieee80211_amrr_node *amn = ni->ni_rctls;
389206358Srpaulo	int retries = *(int *)arg1;
390206358Srpaulo
391343035Savos	if (!amn)
392343035Savos		return;
393343035Savos
394206358Srpaulo	amn->amn_txcnt++;
395206358Srpaulo	if (ok)
396206358Srpaulo		amn->amn_success++;
397206358Srpaulo	amn->amn_retrycnt += retries;
398206358Srpaulo}
399206358Srpaulo
400206358Srpaulo/*
401206358Srpaulo * Set tx count/retry statistics explicitly.  Intended for
402206358Srpaulo * drivers that poll the device for statistics maintained
403206358Srpaulo * in the device.
404206358Srpaulo */
405206358Srpaulostatic void
406206358Srpauloamrr_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni,
407206358Srpaulo    void *arg1, void *arg2, void *arg3)
408206358Srpaulo{
409206358Srpaulo	struct ieee80211_amrr_node *amn = ni->ni_rctls;
410206358Srpaulo	int txcnt = *(int *)arg1, success = *(int *)arg2, retrycnt = *(int *)arg3;
411206358Srpaulo
412359746Seugen	if (!amn)
413359746Seugen		return;
414359746Seugen
415206358Srpaulo	amn->amn_txcnt = txcnt;
416206358Srpaulo	amn->amn_success = success;
417206358Srpaulo	amn->amn_retrycnt = retrycnt;
418206358Srpaulo}
419206358Srpaulo
420164633Ssamstatic int
421178354Ssamamrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
422164633Ssam{
423206358Srpaulo	struct ieee80211vap *vap = arg1;
424206358Srpaulo	struct ieee80211_amrr *amrr = vap->iv_rs;
425343035Savos	int msecs, error;
426178354Ssam
427343035Savos	if (!amrr)
428343035Savos		return ENOMEM;
429343035Savos
430343035Savos	msecs = ticks_to_msecs(amrr->amrr_interval);
431178354Ssam	error = sysctl_handle_int(oidp, &msecs, 0, req);
432178354Ssam	if (error || !req->newptr)
433178354Ssam		return error;
434206358Srpaulo	amrr_setinterval(vap, msecs);
435178354Ssam	return 0;
436164633Ssam}
437164633Ssam
438178354Ssamstatic void
439206358Srpauloamrr_sysctlattach(struct ieee80211vap *vap,
440178354Ssam    struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
441178354Ssam{
442206358Srpaulo	struct ieee80211_amrr *amrr = vap->iv_rs;
443178354Ssam
444343035Savos	if (!amrr)
445343035Savos		return;
446343035Savos
447178354Ssam	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
448206358Srpaulo	    "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
449178354Ssam	    0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
450178354Ssam	/* XXX bounds check values */
451217322Smdf	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
452178354Ssam	    "amrr_max_sucess_threshold", CTLFLAG_RW,
453178354Ssam	    &amrr->amrr_max_success_threshold, 0, "");
454217322Smdf	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
455178354Ssam	    "amrr_min_sucess_threshold", CTLFLAG_RW,
456178354Ssam	    &amrr->amrr_min_success_threshold, 0, "");
457178354Ssam}
458296925Sadrian
459296925Sadrianstatic void
460296925Sadrianamrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
461296925Sadrian{
462296925Sadrian	int rate;
463296925Sadrian	struct ieee80211_amrr_node *amn = ni->ni_rctls;
464296925Sadrian	struct ieee80211_rateset *rs;
465296925Sadrian
466296925Sadrian	/* XXX TODO: check locking? */
467296925Sadrian
468343035Savos	if (!amn)
469343035Savos		return;
470343035Savos
471296925Sadrian	/* XXX TODO: this should be a method */
472296925Sadrian	if (amrr_node_is_11n(ni)) {
473296925Sadrian		rs = (struct ieee80211_rateset *) &ni->ni_htrates;
474296925Sadrian		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
475296925Sadrian		sbuf_printf(s, "rate: MCS %d\n", rate);
476296925Sadrian	} else {
477296925Sadrian		rs = &ni->ni_rates;
478296925Sadrian		rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
479296925Sadrian		sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
480296925Sadrian	}
481296925Sadrian
482296925Sadrian	sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
483296925Sadrian	sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
484296925Sadrian	sbuf_printf(s, "success: %u\n", amn->amn_success);
485296925Sadrian	sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
486296925Sadrian	sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
487296925Sadrian	sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
488296925Sadrian}
489