xref: /dflybsd-src/sys/netproto/802_11/wlan/ieee80211_rssadapt.c (revision 4f655ef568316df0b575f05cebf37546415c0d24)
1085ff963SMatthew Dillon /*	$FreeBSD$	*/
232176cfdSRui Paulo /* $NetBSD: ieee80211_rssadapt.c,v 1.9 2005/02/26 22:45:09 perry Exp $ */
332176cfdSRui Paulo /*-
44028af95SRui Paulo  * Copyright (c) 2010 Rui Paulo <rpaulo@FreeBSD.org>
532176cfdSRui Paulo  * Copyright (c) 2003, 2004 David Young.  All rights reserved.
632176cfdSRui Paulo  *
732176cfdSRui Paulo  * Redistribution and use in source and binary forms, with or
832176cfdSRui Paulo  * without modification, are permitted provided that the following
932176cfdSRui Paulo  * conditions are met:
1032176cfdSRui Paulo  * 1. Redistributions of source code must retain the above copyright
1132176cfdSRui Paulo  *    notice, this list of conditions and the following disclaimer.
1232176cfdSRui Paulo  * 2. Redistributions in binary form must reproduce the above
1332176cfdSRui Paulo  *    copyright notice, this list of conditions and the following
1432176cfdSRui Paulo  *    disclaimer in the documentation and/or other materials provided
1532176cfdSRui Paulo  *    with the distribution.
1632176cfdSRui Paulo  * 3. The name of David Young may not be used to endorse or promote
1732176cfdSRui Paulo  *    products derived from this software without specific prior
1832176cfdSRui Paulo  *    written permission.
1932176cfdSRui Paulo  *
2032176cfdSRui Paulo  * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY
2132176cfdSRui Paulo  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
2232176cfdSRui Paulo  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
2332176cfdSRui Paulo  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL David
2432176cfdSRui Paulo  * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2532176cfdSRui Paulo  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2632176cfdSRui Paulo  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2732176cfdSRui Paulo  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2832176cfdSRui Paulo  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2932176cfdSRui Paulo  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3032176cfdSRui Paulo  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
3132176cfdSRui Paulo  * OF SUCH DAMAGE.
3232176cfdSRui Paulo  */
3332176cfdSRui Paulo #include "opt_wlan.h"
3432176cfdSRui Paulo 
3532176cfdSRui Paulo #include <sys/param.h>
36085ff963SMatthew Dillon #include <sys/systm.h>
3732176cfdSRui Paulo #include <sys/kernel.h>
38085ff963SMatthew Dillon #include <sys/malloc.h>
3932176cfdSRui Paulo #include <sys/module.h>
4032176cfdSRui Paulo #include <sys/socket.h>
4132176cfdSRui Paulo #include <sys/sysctl.h>
4232176cfdSRui Paulo 
4332176cfdSRui Paulo #include <net/if.h>
44085ff963SMatthew Dillon #include <net/if_var.h>
4532176cfdSRui Paulo #include <net/if_media.h>
46085ff963SMatthew Dillon #include <net/ethernet.h>
4732176cfdSRui Paulo 
4832176cfdSRui Paulo #include <netproto/802_11/ieee80211_var.h>
4932176cfdSRui Paulo #include <netproto/802_11/ieee80211_rssadapt.h>
504028af95SRui Paulo #include <netproto/802_11/ieee80211_ratectl.h>
5132176cfdSRui Paulo 
5232176cfdSRui Paulo struct rssadapt_expavgctl {
5332176cfdSRui Paulo 	/* RSS threshold decay. */
5432176cfdSRui Paulo 	u_int rc_decay_denom;
5532176cfdSRui Paulo 	u_int rc_decay_old;
5632176cfdSRui Paulo 	/* RSS threshold update. */
5732176cfdSRui Paulo 	u_int rc_thresh_denom;
5832176cfdSRui Paulo 	u_int rc_thresh_old;
5932176cfdSRui Paulo 	/* RSS average update. */
6032176cfdSRui Paulo 	u_int rc_avgrssi_denom;
6132176cfdSRui Paulo 	u_int rc_avgrssi_old;
6232176cfdSRui Paulo };
6332176cfdSRui Paulo 
6432176cfdSRui Paulo static struct rssadapt_expavgctl master_expavgctl = {
651d8ba4b1SSascha Wildner 	.rc_decay_denom = 16,
661d8ba4b1SSascha Wildner 	.rc_decay_old = 15,
671d8ba4b1SSascha Wildner 	.rc_thresh_denom = 8,
681d8ba4b1SSascha Wildner 	.rc_thresh_old = 4,
691d8ba4b1SSascha Wildner 	.rc_avgrssi_denom = 8,
701d8ba4b1SSascha Wildner 	.rc_avgrssi_old = 4
7132176cfdSRui Paulo };
7232176cfdSRui Paulo 
7332176cfdSRui Paulo #ifdef interpolate
7432176cfdSRui Paulo #undef interpolate
7532176cfdSRui Paulo #endif
7632176cfdSRui Paulo #define interpolate(parm, old, new) ((parm##_old * (old) + \
7732176cfdSRui Paulo                                      (parm##_denom - parm##_old) * (new)) / \
7832176cfdSRui Paulo 				    parm##_denom)
7932176cfdSRui Paulo 
804028af95SRui Paulo static void	rssadapt_setinterval(const struct ieee80211vap *, int);
814028af95SRui Paulo static void	rssadapt_init(struct ieee80211vap *);
824028af95SRui Paulo static void	rssadapt_deinit(struct ieee80211vap *);
834028af95SRui Paulo static void	rssadapt_updatestats(struct ieee80211_rssadapt_node *);
844028af95SRui Paulo static void	rssadapt_node_init(struct ieee80211_node *);
854028af95SRui Paulo static void	rssadapt_node_deinit(struct ieee80211_node *);
864028af95SRui Paulo static int	rssadapt_rate(struct ieee80211_node *, void *, uint32_t);
874028af95SRui Paulo static void	rssadapt_lower_rate(struct ieee80211_rssadapt_node *, int, int);
884028af95SRui Paulo static void	rssadapt_raise_rate(struct ieee80211_rssadapt_node *,
894028af95SRui Paulo 			int, int);
904028af95SRui Paulo static void	rssadapt_tx_complete(const struct ieee80211vap *,
914028af95SRui Paulo     			const struct ieee80211_node *, int,
924028af95SRui Paulo 			void *, void *);
934028af95SRui Paulo static void	rssadapt_sysctlattach(struct ieee80211vap *,
944028af95SRui Paulo 			struct sysctl_ctx_list *, struct sysctl_oid *);
9532176cfdSRui Paulo 
9632176cfdSRui Paulo /* number of references from net80211 layer */
9732176cfdSRui Paulo static	int nrefs = 0;
9832176cfdSRui Paulo 
994028af95SRui Paulo static const struct ieee80211_ratectl rssadapt = {
1004028af95SRui Paulo 	.ir_name	= "rssadapt",
1014028af95SRui Paulo 	.ir_attach	= NULL,
1024028af95SRui Paulo 	.ir_detach	= NULL,
1034028af95SRui Paulo 	.ir_init	= rssadapt_init,
1044028af95SRui Paulo 	.ir_deinit	= rssadapt_deinit,
1054028af95SRui Paulo 	.ir_node_init	= rssadapt_node_init,
1064028af95SRui Paulo 	.ir_node_deinit	= rssadapt_node_deinit,
1074028af95SRui Paulo 	.ir_rate	= rssadapt_rate,
1084028af95SRui Paulo 	.ir_tx_complete	= rssadapt_tx_complete,
1094028af95SRui Paulo 	.ir_tx_update	= NULL,
1104028af95SRui Paulo 	.ir_setinterval	= rssadapt_setinterval,
1114028af95SRui Paulo };
1124028af95SRui Paulo IEEE80211_RATECTL_MODULE(rssadapt, 1);
1134028af95SRui Paulo IEEE80211_RATECTL_ALG(rssadapt, IEEE80211_RATECTL_RSSADAPT, rssadapt);
1144028af95SRui Paulo 
1154028af95SRui Paulo static void
rssadapt_setinterval(const struct ieee80211vap * vap,int msecs)1164028af95SRui Paulo rssadapt_setinterval(const struct ieee80211vap *vap, int msecs)
11732176cfdSRui Paulo {
1184028af95SRui Paulo 	struct ieee80211_rssadapt *rs = vap->iv_rs;
11932176cfdSRui Paulo 	int t;
12032176cfdSRui Paulo 
12132176cfdSRui Paulo 	if (msecs < 100)
12232176cfdSRui Paulo 		msecs = 100;
12332176cfdSRui Paulo 	t = msecs_to_ticks(msecs);
12432176cfdSRui Paulo 	rs->interval = (t < 1) ? 1 : t;
12532176cfdSRui Paulo }
12632176cfdSRui Paulo 
1274028af95SRui Paulo static void
rssadapt_init(struct ieee80211vap * vap)1284028af95SRui Paulo rssadapt_init(struct ieee80211vap *vap)
12932176cfdSRui Paulo {
1304028af95SRui Paulo 	struct ieee80211_rssadapt *rs;
13132176cfdSRui Paulo 
1324028af95SRui Paulo 	KASSERT(vap->iv_rs == NULL, ("%s: iv_rs already initialized",
1334028af95SRui Paulo 	    __func__));
1344028af95SRui Paulo 
135*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
136085ff963SMatthew Dillon 	vap->iv_rs = rs = kmalloc(sizeof(struct ieee80211_rssadapt),
137085ff963SMatthew Dillon 	    M_80211_RATECTL, M_INTWAIT|M_ZERO);
138*4f655ef5SMatthew Dillon #else
139*4f655ef5SMatthew Dillon 	vap->iv_rs = rs = IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt),
140*4f655ef5SMatthew Dillon 	    M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
141*4f655ef5SMatthew Dillon #endif
142085ff963SMatthew Dillon 	if (rs == NULL) {
143085ff963SMatthew Dillon 		if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
144085ff963SMatthew Dillon 		return;
145085ff963SMatthew Dillon 	}
1464028af95SRui Paulo 	rs->vap = vap;
1474028af95SRui Paulo 	rssadapt_setinterval(vap, 500 /* msecs */);
1484028af95SRui Paulo 	rssadapt_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
14932176cfdSRui Paulo }
15032176cfdSRui Paulo 
1514028af95SRui Paulo static void
rssadapt_deinit(struct ieee80211vap * vap)1524028af95SRui Paulo rssadapt_deinit(struct ieee80211vap *vap)
15332176cfdSRui Paulo {
154*4f655ef5SMatthew Dillon 	IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
15532176cfdSRui Paulo }
15632176cfdSRui Paulo 
15732176cfdSRui Paulo static void
rssadapt_updatestats(struct ieee80211_rssadapt_node * ra)15832176cfdSRui Paulo rssadapt_updatestats(struct ieee80211_rssadapt_node *ra)
15932176cfdSRui Paulo {
16032176cfdSRui Paulo 	long interval;
16132176cfdSRui Paulo 
16232176cfdSRui Paulo 	ra->ra_pktrate = (ra->ra_pktrate + 10*(ra->ra_nfail + ra->ra_nok))/2;
16332176cfdSRui Paulo 	ra->ra_nfail = ra->ra_nok = 0;
16432176cfdSRui Paulo 
16532176cfdSRui Paulo 	/*
16632176cfdSRui Paulo 	 * A node is eligible for its rate to be raised every 1/10 to 10
16732176cfdSRui Paulo 	 * seconds, more eligible in proportion to recent packet rates.
16832176cfdSRui Paulo 	 */
16932176cfdSRui Paulo 	interval = MAX(10*1000, 10*1000 / MAX(1, 10 * ra->ra_pktrate));
17032176cfdSRui Paulo 	ra->ra_raise_interval = msecs_to_ticks(interval);
17132176cfdSRui Paulo }
17232176cfdSRui Paulo 
1734028af95SRui Paulo static void
rssadapt_node_init(struct ieee80211_node * ni)1744028af95SRui Paulo rssadapt_node_init(struct ieee80211_node *ni)
17532176cfdSRui Paulo {
1764028af95SRui Paulo 	struct ieee80211_rssadapt_node *ra;
177085ff963SMatthew Dillon 	struct ieee80211vap *vap = ni->ni_vap;
178085ff963SMatthew Dillon 	struct ieee80211_rssadapt *rsa = vap->iv_rs;
17932176cfdSRui Paulo 	const struct ieee80211_rateset *rs = &ni->ni_rates;
18032176cfdSRui Paulo 
181a72c14d0SSascha Wildner 	if (ni->ni_rctls == NULL) {
182a72c14d0SSascha Wildner 		ni->ni_rctls = ra =
183*4f655ef5SMatthew Dillon #if defined(__DragonFly__)
184a72c14d0SSascha Wildner 		    kmalloc(sizeof(struct ieee80211_rssadapt_node),
185085ff963SMatthew Dillon 		        M_80211_RATECTL, M_INTWAIT|M_ZERO);
186*4f655ef5SMatthew Dillon #else
187*4f655ef5SMatthew Dillon 		    IEEE80211_MALLOC(sizeof(struct ieee80211_rssadapt_node),
188*4f655ef5SMatthew Dillon 			M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
189*4f655ef5SMatthew Dillon #endif
190085ff963SMatthew Dillon 		if (ra == NULL) {
191085ff963SMatthew Dillon 			if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
192085ff963SMatthew Dillon 			    "structure\n");
193085ff963SMatthew Dillon 			return;
194a72c14d0SSascha Wildner 		}
195085ff963SMatthew Dillon 	} else
196085ff963SMatthew Dillon 		ra = ni->ni_rctls;
19732176cfdSRui Paulo 	ra->ra_rs = rsa;
19832176cfdSRui Paulo 	ra->ra_rates = *rs;
19932176cfdSRui Paulo 	rssadapt_updatestats(ra);
20032176cfdSRui Paulo 
20132176cfdSRui Paulo 	/* pick initial rate */
20232176cfdSRui Paulo 	for (ra->ra_rix = rs->rs_nrates - 1;
20332176cfdSRui Paulo 	     ra->ra_rix > 0 && (rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL) > 72;
20432176cfdSRui Paulo 	     ra->ra_rix--)
20532176cfdSRui Paulo 		;
20632176cfdSRui Paulo 	ni->ni_txrate = rs->rs_rates[ra->ra_rix] & IEEE80211_RATE_VAL;
20732176cfdSRui Paulo 	ra->ra_ticks = ticks;
20832176cfdSRui Paulo 
20932176cfdSRui Paulo 	IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
21032176cfdSRui Paulo 	    "RSSADAPT initial rate %d", ni->ni_txrate);
21132176cfdSRui Paulo }
21232176cfdSRui Paulo 
2134028af95SRui Paulo static void
rssadapt_node_deinit(struct ieee80211_node * ni)2144028af95SRui Paulo rssadapt_node_deinit(struct ieee80211_node *ni)
2154028af95SRui Paulo {
2164028af95SRui Paulo 
217*4f655ef5SMatthew Dillon 	IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
2184028af95SRui Paulo }
2194028af95SRui Paulo 
22032176cfdSRui Paulo static __inline int
bucket(int pktlen)22132176cfdSRui Paulo bucket(int pktlen)
22232176cfdSRui Paulo {
22332176cfdSRui Paulo 	int i, top, thridx;
22432176cfdSRui Paulo 
22532176cfdSRui Paulo 	for (i = 0, top = IEEE80211_RSSADAPT_BKT0;
22632176cfdSRui Paulo 	     i < IEEE80211_RSSADAPT_BKTS;
22732176cfdSRui Paulo 	     i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) {
22832176cfdSRui Paulo 		thridx = i;
22932176cfdSRui Paulo 		if (pktlen <= top)
23032176cfdSRui Paulo 			break;
23132176cfdSRui Paulo 	}
23232176cfdSRui Paulo 	return thridx;
23332176cfdSRui Paulo }
23432176cfdSRui Paulo 
2354028af95SRui Paulo static int
rssadapt_rate(struct ieee80211_node * ni,void * arg __unused,uint32_t iarg)2364028af95SRui Paulo rssadapt_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg)
23732176cfdSRui Paulo {
2384028af95SRui Paulo 	struct ieee80211_rssadapt_node *ra = ni->ni_rctls;
2394028af95SRui Paulo 	u_int pktlen = iarg;
24032176cfdSRui Paulo 	const struct ieee80211_rateset *rs = &ra->ra_rates;
24132176cfdSRui Paulo 	uint16_t (*thrs)[IEEE80211_RATE_SIZE];
24232176cfdSRui Paulo 	int rix, rssi;
24332176cfdSRui Paulo 
24432176cfdSRui Paulo 	if ((ticks - ra->ra_ticks) > ra->ra_rs->interval) {
24532176cfdSRui Paulo 		rssadapt_updatestats(ra);
24632176cfdSRui Paulo 		ra->ra_ticks = ticks;
24732176cfdSRui Paulo 	}
24832176cfdSRui Paulo 
24932176cfdSRui Paulo 	thrs = &ra->ra_rate_thresh[bucket(pktlen)];
25032176cfdSRui Paulo 
25132176cfdSRui Paulo 	/* XXX this is average rssi, should be using last value */
25232176cfdSRui Paulo 	rssi = ni->ni_ic->ic_node_getrssi(ni);
25332176cfdSRui Paulo 	for (rix = rs->rs_nrates-1; rix >= 0; rix--)
25432176cfdSRui Paulo 		if ((*thrs)[rix] < (rssi << 8))
25532176cfdSRui Paulo 			break;
25632176cfdSRui Paulo 	if (rix != ra->ra_rix) {
25732176cfdSRui Paulo 		/* update public rate */
25832176cfdSRui Paulo 		ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL;
25932176cfdSRui Paulo 		ra->ra_rix = rix;
26032176cfdSRui Paulo 
26132176cfdSRui Paulo 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
26232176cfdSRui Paulo 		    "RSSADAPT new rate %d (pktlen %d rssi %d)",
26332176cfdSRui Paulo 		    ni->ni_txrate, pktlen, rssi);
26432176cfdSRui Paulo 	}
26532176cfdSRui Paulo 	return rix;
26632176cfdSRui Paulo }
26732176cfdSRui Paulo 
26832176cfdSRui Paulo /*
26932176cfdSRui Paulo  * Adapt the data rate to suit the conditions.  When a transmitted
27032176cfdSRui Paulo  * packet is dropped after RAL_RSSADAPT_RETRY_LIMIT retransmissions,
27132176cfdSRui Paulo  * raise the RSS threshold for transmitting packets of similar length at
27232176cfdSRui Paulo  * the same data rate.
27332176cfdSRui Paulo  */
2744028af95SRui Paulo static void
rssadapt_lower_rate(struct ieee80211_rssadapt_node * ra,int pktlen,int rssi)2754028af95SRui Paulo rssadapt_lower_rate(struct ieee80211_rssadapt_node *ra, int pktlen, int rssi)
27632176cfdSRui Paulo {
27732176cfdSRui Paulo 	uint16_t last_thr;
27832176cfdSRui Paulo 	uint16_t (*thrs)[IEEE80211_RATE_SIZE];
27932176cfdSRui Paulo 	u_int rix;
28032176cfdSRui Paulo 
28132176cfdSRui Paulo 	thrs = &ra->ra_rate_thresh[bucket(pktlen)];
28232176cfdSRui Paulo 
28332176cfdSRui Paulo 	rix = ra->ra_rix;
28432176cfdSRui Paulo 	last_thr = (*thrs)[rix];
28532176cfdSRui Paulo 	(*thrs)[rix] = interpolate(master_expavgctl.rc_thresh,
28632176cfdSRui Paulo 	    last_thr, (rssi << 8));
28732176cfdSRui Paulo 
28832176cfdSRui Paulo 	IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
28932176cfdSRui Paulo 	    "RSSADAPT lower threshold for rate %d (last_thr %d new thr %d rssi %d)\n",
29032176cfdSRui Paulo 	    ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
29132176cfdSRui Paulo 	    last_thr, (*thrs)[rix], rssi);
29232176cfdSRui Paulo }
29332176cfdSRui Paulo 
2944028af95SRui Paulo static void
rssadapt_raise_rate(struct ieee80211_rssadapt_node * ra,int pktlen,int rssi)2954028af95SRui Paulo rssadapt_raise_rate(struct ieee80211_rssadapt_node *ra, int pktlen, int rssi)
29632176cfdSRui Paulo {
29732176cfdSRui Paulo 	uint16_t (*thrs)[IEEE80211_RATE_SIZE];
29832176cfdSRui Paulo 	uint16_t newthr, oldthr;
29932176cfdSRui Paulo 	int rix;
30032176cfdSRui Paulo 
30132176cfdSRui Paulo 	thrs = &ra->ra_rate_thresh[bucket(pktlen)];
30232176cfdSRui Paulo 
30332176cfdSRui Paulo 	rix = ra->ra_rix;
30432176cfdSRui Paulo 	if ((*thrs)[rix + 1] > (*thrs)[rix]) {
30532176cfdSRui Paulo 		oldthr = (*thrs)[rix + 1];
30632176cfdSRui Paulo 		if ((*thrs)[rix] == 0)
30732176cfdSRui Paulo 			newthr = (rssi << 8);
30832176cfdSRui Paulo 		else
30932176cfdSRui Paulo 			newthr = (*thrs)[rix];
31032176cfdSRui Paulo 		(*thrs)[rix + 1] = interpolate(master_expavgctl.rc_decay,
31132176cfdSRui Paulo 		    oldthr, newthr);
31232176cfdSRui Paulo 
31332176cfdSRui Paulo 		IEEE80211_DPRINTF(ra->ra_rs->vap, IEEE80211_MSG_RATECTL,
31432176cfdSRui Paulo 		    "RSSADAPT raise threshold for rate %d (oldthr %d newthr %d rssi %d)\n",
31532176cfdSRui Paulo 		    ra->ra_rates.rs_rates[rix + 1] & IEEE80211_RATE_VAL,
31632176cfdSRui Paulo 		    oldthr, newthr, rssi);
31732176cfdSRui Paulo 
31832176cfdSRui Paulo 		ra->ra_last_raise = ticks;
31932176cfdSRui Paulo 	}
32032176cfdSRui Paulo }
32132176cfdSRui Paulo 
3224028af95SRui Paulo static void
rssadapt_tx_complete(const struct ieee80211vap * vap,const struct ieee80211_node * ni,int success,void * arg1,void * arg2)3234028af95SRui Paulo rssadapt_tx_complete(const struct ieee80211vap *vap,
3244028af95SRui Paulo     const struct ieee80211_node *ni, int success, void *arg1, void *arg2)
3254028af95SRui Paulo {
3264028af95SRui Paulo 	struct ieee80211_rssadapt_node *ra = ni->ni_rctls;
3274028af95SRui Paulo 	int pktlen = *(int *)arg1, rssi = *(int *)arg2;
3284028af95SRui Paulo 
3294028af95SRui Paulo 	if (success) {
3304028af95SRui Paulo 		ra->ra_nok++;
3314028af95SRui Paulo 		if ((ra->ra_rix + 1) < ra->ra_rates.rs_nrates &&
3324028af95SRui Paulo 		    (ticks - ra->ra_last_raise) >= ra->ra_raise_interval)
3334028af95SRui Paulo 			rssadapt_raise_rate(ra, pktlen, rssi);
3344028af95SRui Paulo 	} else {
3354028af95SRui Paulo 		ra->ra_nfail++;
3364028af95SRui Paulo 		rssadapt_lower_rate(ra, pktlen, rssi);
3374028af95SRui Paulo 	}
3384028af95SRui Paulo }
3394028af95SRui Paulo 
34032176cfdSRui Paulo static int
rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)34132176cfdSRui Paulo rssadapt_sysctl_interval(SYSCTL_HANDLER_ARGS)
34232176cfdSRui Paulo {
3434028af95SRui Paulo 	struct ieee80211vap *vap = arg1;
3444028af95SRui Paulo 	struct ieee80211_rssadapt *rs = vap->iv_rs;
34532176cfdSRui Paulo 	int msecs = ticks_to_msecs(rs->interval);
34632176cfdSRui Paulo 	int error;
34732176cfdSRui Paulo 
34832176cfdSRui Paulo 	error = sysctl_handle_int(oidp, &msecs, 0, req);
349085ff963SMatthew Dillon 	if (error || !req->newptr)
35047156d48SMatthew Dillon 		return error;
351085ff963SMatthew Dillon 	rssadapt_setinterval(vap, msecs);
352085ff963SMatthew Dillon 	return 0;
35332176cfdSRui Paulo }
35432176cfdSRui Paulo 
35532176cfdSRui Paulo static void
rssadapt_sysctlattach(struct ieee80211vap * vap,struct sysctl_ctx_list * ctx,struct sysctl_oid * tree)3564028af95SRui Paulo rssadapt_sysctlattach(struct ieee80211vap *vap,
35732176cfdSRui Paulo     struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
35832176cfdSRui Paulo {
35932176cfdSRui Paulo 
36032176cfdSRui Paulo 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
3614028af95SRui Paulo 	    "rssadapt_rate_interval", CTLTYPE_INT | CTLFLAG_RW, vap,
36232176cfdSRui Paulo 	    0, rssadapt_sysctl_interval, "I", "rssadapt operation interval (ms)");
36332176cfdSRui Paulo }
364