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>
52*4b2c7dfeSAdrian Chadd #include <net80211/ieee80211_vht.h>
5374828f25SSam Leffler #include <net80211/ieee80211_amrr.h>
54b6108616SRui Paulo #include <net80211/ieee80211_ratectl.h>
5574828f25SSam Leffler
5674828f25SSam Leffler #define is_success(amn) \
5774828f25SSam Leffler ((amn)->amn_retrycnt < (amn)->amn_txcnt / 10)
5874828f25SSam Leffler #define is_failure(amn) \
5974828f25SSam Leffler ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3)
6074828f25SSam Leffler #define is_enough(amn) \
6174828f25SSam Leffler ((amn)->amn_txcnt > 10)
62b032f27cSSam Leffler
63b6108616SRui Paulo static void amrr_setinterval(const struct ieee80211vap *, int);
64b6108616SRui Paulo static void amrr_init(struct ieee80211vap *);
65b6108616SRui Paulo static void amrr_deinit(struct ieee80211vap *);
66b6108616SRui Paulo static void amrr_node_init(struct ieee80211_node *);
67b6108616SRui Paulo static void amrr_node_deinit(struct ieee80211_node *);
68b6108616SRui Paulo static int amrr_update(struct ieee80211_amrr *,
69b6108616SRui Paulo struct ieee80211_amrr_node *, struct ieee80211_node *);
70b6108616SRui Paulo static int amrr_rate(struct ieee80211_node *, void *, uint32_t);
71f6930becSAndriy Voskoboinyk static void amrr_tx_complete(const struct ieee80211_node *,
72f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *);
73f6930becSAndriy Voskoboinyk static void amrr_tx_update_cb(void *, struct ieee80211_node *);
74f6930becSAndriy Voskoboinyk static void amrr_tx_update(struct ieee80211vap *vap,
75f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *);
76b6108616SRui Paulo static void amrr_sysctlattach(struct ieee80211vap *,
77b6108616SRui Paulo struct sysctl_ctx_list *, struct sysctl_oid *);
7873931706SAdrian Chadd static void amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s);
79b032f27cSSam Leffler
80b032f27cSSam Leffler /* number of references from net80211 layer */
81b032f27cSSam Leffler static int nrefs = 0;
82b032f27cSSam Leffler
83b6108616SRui Paulo static const struct ieee80211_ratectl amrr = {
84b6108616SRui Paulo .ir_name = "amrr",
85b6108616SRui Paulo .ir_attach = NULL,
86b6108616SRui Paulo .ir_detach = NULL,
87b6108616SRui Paulo .ir_init = amrr_init,
88b6108616SRui Paulo .ir_deinit = amrr_deinit,
89b6108616SRui Paulo .ir_node_init = amrr_node_init,
90b6108616SRui Paulo .ir_node_deinit = amrr_node_deinit,
91b6108616SRui Paulo .ir_rate = amrr_rate,
92b6108616SRui Paulo .ir_tx_complete = amrr_tx_complete,
93b6108616SRui Paulo .ir_tx_update = amrr_tx_update,
94b6108616SRui Paulo .ir_setinterval = amrr_setinterval,
9573931706SAdrian Chadd .ir_node_stats = amrr_node_stats,
96b6108616SRui Paulo };
97b6108616SRui Paulo IEEE80211_RATECTL_MODULE(amrr, 1);
98b6108616SRui Paulo IEEE80211_RATECTL_ALG(amrr, IEEE80211_RATECTL_AMRR, amrr);
99b6108616SRui Paulo
100b6108616SRui Paulo static void
amrr_setinterval(const struct ieee80211vap * vap,int msecs)101b6108616SRui Paulo amrr_setinterval(const struct ieee80211vap *vap, int msecs)
102b032f27cSSam Leffler {
103b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
104b032f27cSSam Leffler
1054367c2d1SAndriy Voskoboinyk if (!amrr)
1064367c2d1SAndriy Voskoboinyk return;
1074367c2d1SAndriy Voskoboinyk
108b032f27cSSam Leffler if (msecs < 100)
109b032f27cSSam Leffler msecs = 100;
1109df9e936SAndriy Voskoboinyk amrr->amrr_interval = msecs_to_ticks(msecs);
111b032f27cSSam Leffler }
11274828f25SSam Leffler
113b6108616SRui Paulo static void
amrr_init(struct ieee80211vap * vap)114b6108616SRui Paulo amrr_init(struct ieee80211vap *vap)
11574828f25SSam Leffler {
116b6108616SRui Paulo struct ieee80211_amrr *amrr;
117b032f27cSSam Leffler
118b6108616SRui Paulo KASSERT(vap->iv_rs == NULL, ("%s called multiple times", __func__));
119b6108616SRui Paulo
120810490a0SAndriy Voskoboinyk nrefs++; /* XXX locking */
121b9b53389SAdrian Chadd amrr = vap->iv_rs = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr),
122b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
123645fe19aSRui Paulo if (amrr == NULL) {
124645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc ratectl structure\n");
125645fe19aSRui Paulo return;
126645fe19aSRui Paulo }
127b6108616SRui Paulo amrr->amrr_min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD;
128b6108616SRui Paulo amrr->amrr_max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD;
129b6108616SRui Paulo amrr_setinterval(vap, 500 /* ms */);
130b6108616SRui Paulo amrr_sysctlattach(vap, vap->iv_sysctl, vap->iv_oid);
131b032f27cSSam Leffler }
132b032f27cSSam Leffler
133b6108616SRui Paulo static void
amrr_deinit(struct ieee80211vap * vap)134b6108616SRui Paulo amrr_deinit(struct ieee80211vap *vap)
135b032f27cSSam Leffler {
136810490a0SAndriy Voskoboinyk KASSERT(nrefs > 0, ("imbalanced attach/detach"));
137cd9fee3dSBjoern A. Zeeb IEEE80211_FREE(vap->iv_rs, M_80211_RATECTL);
138cd9fee3dSBjoern A. Zeeb vap->iv_rs = NULL; /* guard */
139810490a0SAndriy Voskoboinyk nrefs--; /* XXX locking */
14074828f25SSam Leffler }
14174828f25SSam Leffler
142b6108616SRui Paulo static void
amrr_node_init_vht(struct ieee80211_node * ni)143*4b2c7dfeSAdrian Chadd amrr_node_init_vht(struct ieee80211_node *ni)
144*4b2c7dfeSAdrian Chadd {
145*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
146*4b2c7dfeSAdrian Chadd
147*4b2c7dfeSAdrian Chadd /* Default to VHT NSS 1 MCS 2; should be reliable! */
148*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs = 2;
149*4b2c7dfeSAdrian Chadd amn->amn_vht_nss = 1;
150*4b2c7dfeSAdrian Chadd ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
151*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs);
152*4b2c7dfeSAdrian Chadd
153*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
154*4b2c7dfeSAdrian Chadd "AMRR: VHT: initial rate NSS %d MCS %d",
155*4b2c7dfeSAdrian Chadd amn->amn_vht_nss,
156*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs);
157*4b2c7dfeSAdrian Chadd }
158*4b2c7dfeSAdrian Chadd
159*4b2c7dfeSAdrian Chadd static void
amrr_node_init_ht(struct ieee80211_node * ni)160078acac8SAdrian Chadd amrr_node_init_ht(struct ieee80211_node *ni)
161078acac8SAdrian Chadd {
162078acac8SAdrian Chadd const struct ieee80211_rateset *rs;
163078acac8SAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
164078acac8SAdrian Chadd uint8_t rate; /* dot11rate */
165078acac8SAdrian Chadd
166078acac8SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
167078acac8SAdrian Chadd /* Initial rate - lowest */
168078acac8SAdrian Chadd rate = rs->rs_rates[0];
169078acac8SAdrian Chadd
170078acac8SAdrian Chadd /* Pick something low that's likely to succeed */
171078acac8SAdrian Chadd for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
172078acac8SAdrian Chadd amn->amn_rix--) {
173078acac8SAdrian Chadd /* 11n - stop at MCS4 */
174078acac8SAdrian Chadd if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4)
175078acac8SAdrian Chadd break;
176078acac8SAdrian Chadd }
177078acac8SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
178078acac8SAdrian Chadd
179078acac8SAdrian Chadd /* Ensure the MCS bit is set */
180078acac8SAdrian Chadd rate |= IEEE80211_RATE_MCS;
181078acac8SAdrian Chadd
182078acac8SAdrian Chadd /* Assign initial rate from the rateset */
183078acac8SAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, rate);
184078acac8SAdrian Chadd
185078acac8SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
186078acac8SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
187078acac8SAdrian Chadd "AMRR: nrates=%d, initial rate MCS %d",
188078acac8SAdrian Chadd rs->rs_nrates,
189078acac8SAdrian Chadd (rate & IEEE80211_RATE_VAL));
190078acac8SAdrian Chadd }
191078acac8SAdrian Chadd
192078acac8SAdrian Chadd static void
amrr_node_init_legacy(struct ieee80211_node * ni)193078acac8SAdrian Chadd amrr_node_init_legacy(struct ieee80211_node *ni)
194078acac8SAdrian Chadd {
195078acac8SAdrian Chadd const struct ieee80211_rateset *rs;
196078acac8SAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
197078acac8SAdrian Chadd uint8_t rate; /* dot11rate */
198078acac8SAdrian Chadd
199078acac8SAdrian Chadd rs = &ni->ni_rates;
200078acac8SAdrian Chadd /* Initial rate - lowest */
201078acac8SAdrian Chadd rate = rs->rs_rates[0];
202078acac8SAdrian Chadd
203078acac8SAdrian Chadd /* Clear the basic rate flag if it's not 11n */
204078acac8SAdrian Chadd rate &= IEEE80211_RATE_VAL;
205078acac8SAdrian Chadd
206078acac8SAdrian Chadd /* Pick something low that's likely to succeed */
207078acac8SAdrian Chadd for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0;
208078acac8SAdrian Chadd amn->amn_rix--) {
209078acac8SAdrian Chadd /* legacy - anything < 36mbit, stop searching */
210078acac8SAdrian Chadd if ((rs->rs_rates[amn->amn_rix] &
211078acac8SAdrian Chadd IEEE80211_RATE_VAL) <= 72)
212078acac8SAdrian Chadd break;
213078acac8SAdrian Chadd }
214078acac8SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
215078acac8SAdrian Chadd
216078acac8SAdrian Chadd /* Assign initial rate from the rateset */
217078acac8SAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, rate);
218078acac8SAdrian Chadd
219078acac8SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
220078acac8SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
221078acac8SAdrian Chadd "AMRR: nrates=%d, initial rate %d Mb",
222078acac8SAdrian Chadd rs->rs_nrates,
223078acac8SAdrian Chadd (rate & IEEE80211_RATE_VAL) / 2);
224078acac8SAdrian Chadd }
225078acac8SAdrian Chadd
226078acac8SAdrian Chadd static void
amrr_node_init(struct ieee80211_node * ni)227b6108616SRui Paulo amrr_node_init(struct ieee80211_node *ni)
22874828f25SSam Leffler {
229b6108616SRui Paulo struct ieee80211vap *vap = ni->ni_vap;
230b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
231b6108616SRui Paulo struct ieee80211_amrr_node *amn;
232b032f27cSSam Leffler
2334367c2d1SAndriy Voskoboinyk if (!amrr) {
2344367c2d1SAndriy Voskoboinyk if_printf(vap->iv_ifp, "ratectl structure was not allocated, "
2354367c2d1SAndriy Voskoboinyk "per-node structure allocation skipped\n");
2364367c2d1SAndriy Voskoboinyk return;
2374367c2d1SAndriy Voskoboinyk }
2384367c2d1SAndriy Voskoboinyk
239380fe2dfSRui Paulo if (ni->ni_rctls == NULL) {
240b9b53389SAdrian Chadd ni->ni_rctls = amn = IEEE80211_MALLOC(sizeof(struct ieee80211_amrr_node),
241b9b53389SAdrian Chadd M_80211_RATECTL, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
242645fe19aSRui Paulo if (amn == NULL) {
243645fe19aSRui Paulo if_printf(vap->iv_ifp, "couldn't alloc per-node ratectl "
244645fe19aSRui Paulo "structure\n");
245645fe19aSRui Paulo return;
246645fe19aSRui Paulo }
247380fe2dfSRui Paulo } else
248380fe2dfSRui Paulo amn = ni->ni_rctls;
249078acac8SAdrian Chadd
250078acac8SAdrian Chadd /* Common state */
251b032f27cSSam Leffler amn->amn_amrr = amrr;
25274828f25SSam Leffler amn->amn_success = 0;
25374828f25SSam Leffler amn->amn_recovery = 0;
25474828f25SSam Leffler amn->amn_txcnt = amn->amn_retrycnt = 0;
25574828f25SSam Leffler amn->amn_success_threshold = amrr->amrr_min_success_threshold;
256b032f27cSSam Leffler amn->amn_ticks = ticks;
257b032f27cSSam Leffler
258*4b2c7dfeSAdrian Chadd /* Pick the right rateset */
259*4b2c7dfeSAdrian Chadd if (ieee80211_vht_check_tx_vht(ni))
260*4b2c7dfeSAdrian Chadd amrr_node_init_vht(ni);
261*4b2c7dfeSAdrian Chadd else if (ieee80211_ht_check_tx_ht(ni))
262078acac8SAdrian Chadd amrr_node_init_ht(ni);
263078acac8SAdrian Chadd else
264078acac8SAdrian Chadd amrr_node_init_legacy(ni);
26574828f25SSam Leffler }
26674828f25SSam Leffler
267b6108616SRui Paulo static void
amrr_node_deinit(struct ieee80211_node * ni)268b6108616SRui Paulo amrr_node_deinit(struct ieee80211_node *ni)
269b6108616SRui Paulo {
270b9b53389SAdrian Chadd IEEE80211_FREE(ni->ni_rctls, M_80211_RATECTL);
271b6108616SRui Paulo }
272b6108616SRui Paulo
273*4b2c7dfeSAdrian Chadd static void
amrr_update_vht_inc(struct ieee80211_node * ni)274*4b2c7dfeSAdrian Chadd amrr_update_vht_inc(struct ieee80211_node *ni)
275*4b2c7dfeSAdrian Chadd {
276*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
277*4b2c7dfeSAdrian Chadd uint8_t nss, mcs;
278*4b2c7dfeSAdrian Chadd
279*4b2c7dfeSAdrian Chadd /*
280*4b2c7dfeSAdrian Chadd * For now just keep looping over MCS to 9, then NSS up, checking if
281*4b2c7dfeSAdrian Chadd * it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
282*4b2c7dfeSAdrian Chadd * until we hit max. This at least tests the VHT MCS rates,
283*4b2c7dfeSAdrian Chadd * but definitely is suboptimal (in the same way the 11n MCS selection
284*4b2c7dfeSAdrian Chadd * is suboptimal.)
285*4b2c7dfeSAdrian Chadd */
286*4b2c7dfeSAdrian Chadd nss = amn->amn_vht_nss;
287*4b2c7dfeSAdrian Chadd mcs = amn->amn_vht_mcs;
288*4b2c7dfeSAdrian Chadd
289*4b2c7dfeSAdrian Chadd while (nss <= 8 && mcs <= 9) {
290*4b2c7dfeSAdrian Chadd /* Increment MCS 0..9, NSS 1..8 */
291*4b2c7dfeSAdrian Chadd if (mcs == 9) {
292*4b2c7dfeSAdrian Chadd mcs = 0;
293*4b2c7dfeSAdrian Chadd nss++;
294*4b2c7dfeSAdrian Chadd } else
295*4b2c7dfeSAdrian Chadd mcs++;
296*4b2c7dfeSAdrian Chadd if (nss > 8)
297*4b2c7dfeSAdrian Chadd break;
298*4b2c7dfeSAdrian Chadd
299*4b2c7dfeSAdrian Chadd if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
300*4b2c7dfeSAdrian Chadd mcs)) {
301*4b2c7dfeSAdrian Chadd amn->amn_vht_nss = nss;
302*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs = mcs;
303*4b2c7dfeSAdrian Chadd break;
304*4b2c7dfeSAdrian Chadd }
305*4b2c7dfeSAdrian Chadd }
306*4b2c7dfeSAdrian Chadd }
307*4b2c7dfeSAdrian Chadd
308*4b2c7dfeSAdrian Chadd static void
amrr_update_vht_dec(struct ieee80211_node * ni)309*4b2c7dfeSAdrian Chadd amrr_update_vht_dec(struct ieee80211_node *ni)
310*4b2c7dfeSAdrian Chadd {
311*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
312*4b2c7dfeSAdrian Chadd uint8_t nss, mcs;
313*4b2c7dfeSAdrian Chadd
314*4b2c7dfeSAdrian Chadd /*
315*4b2c7dfeSAdrian Chadd * For now just keep looping over MCS 9 .. 0 then NSS down, checking if
316*4b2c7dfeSAdrian Chadd * it's valid via ieee80211_vht_node_check_tx_valid_mcs(),
317*4b2c7dfeSAdrian Chadd * until we hit min. This at least tests the VHT MCS rates,
318*4b2c7dfeSAdrian Chadd * but definitely is suboptimal (in the same way the 11n MCS selection
319*4b2c7dfeSAdrian Chadd * is suboptimal.
320*4b2c7dfeSAdrian Chadd */
321*4b2c7dfeSAdrian Chadd nss = amn->amn_vht_nss;
322*4b2c7dfeSAdrian Chadd mcs = amn->amn_vht_mcs;
323*4b2c7dfeSAdrian Chadd
324*4b2c7dfeSAdrian Chadd while (nss >= 1 && mcs >= 0) {
325*4b2c7dfeSAdrian Chadd
326*4b2c7dfeSAdrian Chadd if (mcs == 0) {
327*4b2c7dfeSAdrian Chadd mcs = 9;
328*4b2c7dfeSAdrian Chadd nss--;
329*4b2c7dfeSAdrian Chadd } else
330*4b2c7dfeSAdrian Chadd mcs--;
331*4b2c7dfeSAdrian Chadd if (nss < 1)
332*4b2c7dfeSAdrian Chadd break;
333*4b2c7dfeSAdrian Chadd
334*4b2c7dfeSAdrian Chadd if (ieee80211_vht_node_check_tx_valid_mcs(ni, ni->ni_chw, nss,
335*4b2c7dfeSAdrian Chadd mcs)) {
336*4b2c7dfeSAdrian Chadd amn->amn_vht_nss = nss;
337*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs = mcs;
338*4b2c7dfeSAdrian Chadd break;
339*4b2c7dfeSAdrian Chadd }
340*4b2c7dfeSAdrian Chadd }
341*4b2c7dfeSAdrian Chadd }
342*4b2c7dfeSAdrian Chadd
343*4b2c7dfeSAdrian Chadd /*
344*4b2c7dfeSAdrian Chadd * A placeholder / temporary hack VHT rate control.
345*4b2c7dfeSAdrian Chadd *
346*4b2c7dfeSAdrian Chadd * Use the available MCS rates at the current node bandwidth
347*4b2c7dfeSAdrian Chadd * and configured / negotiated MCS rates.
348*4b2c7dfeSAdrian Chadd */
349*4b2c7dfeSAdrian Chadd static int
amrr_update_vht(struct ieee80211_node * ni)350*4b2c7dfeSAdrian Chadd amrr_update_vht(struct ieee80211_node *ni)
351*4b2c7dfeSAdrian Chadd {
352*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
353*4b2c7dfeSAdrian Chadd struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
354*4b2c7dfeSAdrian Chadd
355*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
356*4b2c7dfeSAdrian Chadd "AMRR: VHT: current rate NSS %d MCS %d, txcnt=%d, retrycnt=%d",
357*4b2c7dfeSAdrian Chadd amn->amn_vht_nss, amn->amn_vht_mcs, amn->amn_txcnt,
358*4b2c7dfeSAdrian Chadd amn->amn_retrycnt);
359*4b2c7dfeSAdrian Chadd
360*4b2c7dfeSAdrian Chadd if (is_success(amn)) {
361*4b2c7dfeSAdrian Chadd amn->amn_success++;
362*4b2c7dfeSAdrian Chadd if (amn->amn_success >= amn->amn_success_threshold) {
363*4b2c7dfeSAdrian Chadd amn->amn_recovery = 1;
364*4b2c7dfeSAdrian Chadd amn->amn_success = 0;
365*4b2c7dfeSAdrian Chadd
366*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
367*4b2c7dfeSAdrian Chadd "AMRR: VHT: increase rate (txcnt=%d retrycnt=%d)",
368*4b2c7dfeSAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt);
369*4b2c7dfeSAdrian Chadd
370*4b2c7dfeSAdrian Chadd amrr_update_vht_inc(ni);
371*4b2c7dfeSAdrian Chadd } else {
372*4b2c7dfeSAdrian Chadd amn->amn_recovery = 0;
373*4b2c7dfeSAdrian Chadd }
374*4b2c7dfeSAdrian Chadd } else if (is_failure(amn)) {
375*4b2c7dfeSAdrian Chadd amn->amn_success = 0;
376*4b2c7dfeSAdrian Chadd
377*4b2c7dfeSAdrian Chadd if (amn->amn_recovery) {
378*4b2c7dfeSAdrian Chadd amn->amn_success_threshold *= 2;
379*4b2c7dfeSAdrian Chadd if (amn->amn_success_threshold >
380*4b2c7dfeSAdrian Chadd amrr->amrr_max_success_threshold)
381*4b2c7dfeSAdrian Chadd amn->amn_success_threshold =
382*4b2c7dfeSAdrian Chadd amrr->amrr_max_success_threshold;
383*4b2c7dfeSAdrian Chadd } else {
384*4b2c7dfeSAdrian Chadd amn->amn_success_threshold =
385*4b2c7dfeSAdrian Chadd amrr->amrr_min_success_threshold;
386*4b2c7dfeSAdrian Chadd }
387*4b2c7dfeSAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
388*4b2c7dfeSAdrian Chadd "AMRR: VHT: decreasing rate (txcnt=%d retrycnt=%d)",
389*4b2c7dfeSAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt);
390*4b2c7dfeSAdrian Chadd
391*4b2c7dfeSAdrian Chadd amrr_update_vht_dec(ni);
392*4b2c7dfeSAdrian Chadd
393*4b2c7dfeSAdrian Chadd amn->amn_recovery = 0;
394*4b2c7dfeSAdrian Chadd }
395*4b2c7dfeSAdrian Chadd
396*4b2c7dfeSAdrian Chadd /* Reset counters */
397*4b2c7dfeSAdrian Chadd amn->amn_txcnt = 0;
398*4b2c7dfeSAdrian Chadd amn->amn_retrycnt = 0;
399*4b2c7dfeSAdrian Chadd
400*4b2c7dfeSAdrian Chadd /* Return 0, not useful anymore */
401*4b2c7dfeSAdrian Chadd return (0);
402*4b2c7dfeSAdrian Chadd }
403*4b2c7dfeSAdrian Chadd
404b032f27cSSam Leffler static int
amrr_update_ht(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)405e99cbea4SAdrian Chadd amrr_update_ht(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
406b032f27cSSam Leffler struct ieee80211_node *ni)
40774828f25SSam Leffler {
408b032f27cSSam Leffler int rix = amn->amn_rix;
409e99cbea4SAdrian Chadd const struct ieee80211_rateset *rs;
41074828f25SSam Leffler
411f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *)&ni->ni_htrates;
412f8bf74f2SAdrian Chadd
413eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
414f8bf74f2SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
415e99cbea4SAdrian Chadd "AMRR: current rate MCS %d, txcnt=%d, retrycnt=%d",
416f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL,
417f8bf74f2SAdrian Chadd amn->amn_txcnt,
418f8bf74f2SAdrian Chadd amn->amn_retrycnt);
419f8bf74f2SAdrian Chadd
420f8d390beSAdrian Chadd /*
421f8d390beSAdrian Chadd * XXX This is totally bogus for 11n, as although high MCS
422f8d390beSAdrian Chadd * rates for each stream may be failing, the next stream
423f8d390beSAdrian Chadd * should be checked.
424f8d390beSAdrian Chadd *
425f8d390beSAdrian Chadd * Eg, if MCS5 is ok but MCS6/7 isn't, and we can go up to
426f8d390beSAdrian Chadd * MCS23, we should skip 6/7 and try 8 onwards.
427f8d390beSAdrian Chadd */
428b032f27cSSam Leffler if (is_success(amn)) {
42974828f25SSam Leffler amn->amn_success++;
43074828f25SSam Leffler if (amn->amn_success >= amn->amn_success_threshold &&
431f8bf74f2SAdrian Chadd rix + 1 < rs->rs_nrates) {
43274828f25SSam Leffler amn->amn_recovery = 1;
43374828f25SSam Leffler amn->amn_success = 0;
434b032f27cSSam Leffler rix++;
435eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
436b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
437e99cbea4SAdrian Chadd "AMRR increasing rate MCS %d "
438e99cbea4SAdrian Chadd "(txcnt=%d retrycnt=%d)",
439f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL,
44074828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt);
44174828f25SSam Leffler } else {
44274828f25SSam Leffler amn->amn_recovery = 0;
44374828f25SSam Leffler }
44474828f25SSam Leffler } else if (is_failure(amn)) {
44574828f25SSam Leffler amn->amn_success = 0;
446b032f27cSSam Leffler if (rix > 0) {
44774828f25SSam Leffler if (amn->amn_recovery) {
44874828f25SSam Leffler amn->amn_success_threshold *= 2;
44974828f25SSam Leffler if (amn->amn_success_threshold >
45074828f25SSam Leffler amrr->amrr_max_success_threshold)
45174828f25SSam Leffler amn->amn_success_threshold =
45274828f25SSam Leffler amrr->amrr_max_success_threshold;
45374828f25SSam Leffler } else {
45474828f25SSam Leffler amn->amn_success_threshold =
45574828f25SSam Leffler amrr->amrr_min_success_threshold;
45674828f25SSam Leffler }
457b032f27cSSam Leffler rix--;
458eec88845SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
459b032f27cSSam Leffler IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
460e99cbea4SAdrian Chadd "AMRR decreasing rate MCS %d "
461e99cbea4SAdrian Chadd "(txcnt=%d retrycnt=%d)",
462f8bf74f2SAdrian Chadd rs->rs_rates[rix] & IEEE80211_RATE_VAL,
46374828f25SSam Leffler amn->amn_txcnt, amn->amn_retrycnt);
46474828f25SSam Leffler }
46574828f25SSam Leffler amn->amn_recovery = 0;
46674828f25SSam Leffler }
46774828f25SSam Leffler
468e99cbea4SAdrian Chadd return (rix);
469e99cbea4SAdrian Chadd }
470e99cbea4SAdrian Chadd
471e99cbea4SAdrian Chadd static int
amrr_update_legacy(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)472e99cbea4SAdrian Chadd amrr_update_legacy(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
473e99cbea4SAdrian Chadd struct ieee80211_node *ni)
474e99cbea4SAdrian Chadd {
475e99cbea4SAdrian Chadd int rix = amn->amn_rix;
476e99cbea4SAdrian Chadd const struct ieee80211_rateset *rs;
477e99cbea4SAdrian Chadd
478e99cbea4SAdrian Chadd rs = &ni->ni_rates;
479e99cbea4SAdrian Chadd
480e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
481e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
482e99cbea4SAdrian Chadd "AMRR: current rate %d Mb, txcnt=%d, retrycnt=%d",
483e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
484e99cbea4SAdrian Chadd amn->amn_txcnt,
485e99cbea4SAdrian Chadd amn->amn_retrycnt);
486e99cbea4SAdrian Chadd
487e99cbea4SAdrian Chadd if (is_success(amn)) {
488e99cbea4SAdrian Chadd amn->amn_success++;
489e99cbea4SAdrian Chadd if (amn->amn_success >= amn->amn_success_threshold &&
490e99cbea4SAdrian Chadd rix + 1 < rs->rs_nrates) {
491e99cbea4SAdrian Chadd amn->amn_recovery = 1;
492e99cbea4SAdrian Chadd amn->amn_success = 0;
493e99cbea4SAdrian Chadd rix++;
494e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
495e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
496e99cbea4SAdrian Chadd "AMRR increasing rate %d Mb (txcnt=%d retrycnt=%d)",
497e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
498e99cbea4SAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt);
499e99cbea4SAdrian Chadd } else {
500e99cbea4SAdrian Chadd amn->amn_recovery = 0;
501e99cbea4SAdrian Chadd }
502e99cbea4SAdrian Chadd } else if (is_failure(amn)) {
503e99cbea4SAdrian Chadd amn->amn_success = 0;
504e99cbea4SAdrian Chadd if (rix > 0) {
505e99cbea4SAdrian Chadd if (amn->amn_recovery) {
506e99cbea4SAdrian Chadd amn->amn_success_threshold *= 2;
507e99cbea4SAdrian Chadd if (amn->amn_success_threshold >
508e99cbea4SAdrian Chadd amrr->amrr_max_success_threshold)
509e99cbea4SAdrian Chadd amn->amn_success_threshold =
510e99cbea4SAdrian Chadd amrr->amrr_max_success_threshold;
511e99cbea4SAdrian Chadd } else {
512e99cbea4SAdrian Chadd amn->amn_success_threshold =
513e99cbea4SAdrian Chadd amrr->amrr_min_success_threshold;
514e99cbea4SAdrian Chadd }
515e99cbea4SAdrian Chadd rix--;
516e99cbea4SAdrian Chadd /* XXX TODO: we really need a rate-to-string method */
517e99cbea4SAdrian Chadd IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_RATECTL, ni,
518e99cbea4SAdrian Chadd "AMRR decreasing rate %d Mb (txcnt=%d retrycnt=%d)",
519e99cbea4SAdrian Chadd (rs->rs_rates[rix] & IEEE80211_RATE_VAL) / 2,
520e99cbea4SAdrian Chadd amn->amn_txcnt, amn->amn_retrycnt);
521e99cbea4SAdrian Chadd }
522e99cbea4SAdrian Chadd amn->amn_recovery = 0;
523e99cbea4SAdrian Chadd }
524e99cbea4SAdrian Chadd
525e99cbea4SAdrian Chadd return (rix);
526e99cbea4SAdrian Chadd }
527e99cbea4SAdrian Chadd
528e99cbea4SAdrian Chadd static int
amrr_update(struct ieee80211_amrr * amrr,struct ieee80211_amrr_node * amn,struct ieee80211_node * ni)529e99cbea4SAdrian Chadd amrr_update(struct ieee80211_amrr *amrr, struct ieee80211_amrr_node *amn,
530e99cbea4SAdrian Chadd struct ieee80211_node *ni)
531e99cbea4SAdrian Chadd {
532e99cbea4SAdrian Chadd int rix;
533e99cbea4SAdrian Chadd
534e99cbea4SAdrian Chadd KASSERT(is_enough(amn), ("txcnt %d", amn->amn_txcnt));
535e99cbea4SAdrian Chadd
536*4b2c7dfeSAdrian Chadd /* Pick the right rateset */
537*4b2c7dfeSAdrian Chadd if (ieee80211_vht_check_tx_vht(ni))
538*4b2c7dfeSAdrian Chadd rix = amrr_update_vht(ni);
539*4b2c7dfeSAdrian Chadd else if (ieee80211_ht_check_tx_ht(ni))
540e99cbea4SAdrian Chadd rix = amrr_update_ht(amrr, amn, ni);
541e99cbea4SAdrian Chadd else
542e99cbea4SAdrian Chadd rix = amrr_update_legacy(amrr, amn, ni);
543e99cbea4SAdrian Chadd
544b032f27cSSam Leffler /* reset counters */
545b032f27cSSam Leffler amn->amn_txcnt = 0;
546b032f27cSSam Leffler amn->amn_retrycnt = 0;
547b032f27cSSam Leffler
548e99cbea4SAdrian Chadd return (rix);
549b032f27cSSam Leffler }
550b032f27cSSam Leffler
551*4b2c7dfeSAdrian Chadd static int
amrr_rate_vht(struct ieee80211_node * ni)552*4b2c7dfeSAdrian Chadd amrr_rate_vht(struct ieee80211_node *ni)
553*4b2c7dfeSAdrian Chadd {
554*4b2c7dfeSAdrian Chadd struct ieee80211_amrr *amrr = ni->ni_vap->iv_rs;
555*4b2c7dfeSAdrian Chadd struct ieee80211_amrr_node *amn = ni->ni_rctls;
556*4b2c7dfeSAdrian Chadd
557*4b2c7dfeSAdrian Chadd if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval)
558*4b2c7dfeSAdrian Chadd amrr_update_vht(ni);
559*4b2c7dfeSAdrian Chadd
560*4b2c7dfeSAdrian Chadd ieee80211_node_set_txrate_vht_rate(ni, amn->amn_vht_nss,
561*4b2c7dfeSAdrian Chadd amn->amn_vht_mcs);
562*4b2c7dfeSAdrian Chadd
563*4b2c7dfeSAdrian Chadd /* Note: There's no vht rs_rates, and the API doesn't use it anymore */
564*4b2c7dfeSAdrian Chadd return (0);
565*4b2c7dfeSAdrian Chadd }
566*4b2c7dfeSAdrian Chadd
567b032f27cSSam Leffler /*
568b032f27cSSam Leffler * Return the rate index to use in sending a data frame.
569b032f27cSSam Leffler * Update our internal state if it's been long enough.
570b032f27cSSam Leffler * If the rate changes we also update ni_txrate to match.
571b032f27cSSam Leffler */
572b6108616SRui Paulo static int
amrr_rate(struct ieee80211_node * ni,void * arg __unused,uint32_t iarg __unused)573b6108616SRui Paulo amrr_rate(struct ieee80211_node *ni, void *arg __unused, uint32_t iarg __unused)
574b032f27cSSam Leffler {
575b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls;
5764367c2d1SAndriy Voskoboinyk struct ieee80211_amrr *amrr;
577f8bf74f2SAdrian Chadd const struct ieee80211_rateset *rs = NULL;
578b032f27cSSam Leffler int rix;
579b032f27cSSam Leffler
5804367c2d1SAndriy Voskoboinyk /* XXX should return -1 here, but drivers may not expect this... */
5814367c2d1SAndriy Voskoboinyk if (!amn)
5824367c2d1SAndriy Voskoboinyk {
58338075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni,
58438075f7dSAdrian Chadd ni->ni_rates.rs_rates[0]);
5854367c2d1SAndriy Voskoboinyk return 0;
5864367c2d1SAndriy Voskoboinyk }
5874367c2d1SAndriy Voskoboinyk
588*4b2c7dfeSAdrian Chadd if (ieee80211_vht_check_tx_vht(ni))
589*4b2c7dfeSAdrian Chadd return (amrr_rate_vht(ni));
5904367c2d1SAndriy Voskoboinyk
591*4b2c7dfeSAdrian Chadd /* Pick the right rateset */
5922014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
593f8bf74f2SAdrian Chadd /* XXX ew */
594f8bf74f2SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
595f8bf74f2SAdrian Chadd } else {
596f8bf74f2SAdrian Chadd rs = &ni->ni_rates;
597f8bf74f2SAdrian Chadd }
598f8bf74f2SAdrian Chadd
599*4b2c7dfeSAdrian Chadd amrr = amn->amn_amrr;
600b032f27cSSam Leffler if (is_enough(amn) && (ticks - amn->amn_ticks) > amrr->amrr_interval) {
601b032f27cSSam Leffler rix = amrr_update(amrr, amn, ni);
602b032f27cSSam Leffler if (rix != amn->amn_rix) {
60338075f7dSAdrian Chadd uint8_t dot11Rate;
604b032f27cSSam Leffler /* update public rate */
60538075f7dSAdrian Chadd dot11Rate = rs->rs_rates[rix];
606f8bf74f2SAdrian Chadd /* XXX strip basic rate flag from txrate, if non-11n */
6072014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni))
60838075f7dSAdrian Chadd dot11Rate |= IEEE80211_RATE_MCS;
609f8bf74f2SAdrian Chadd else
61038075f7dSAdrian Chadd dot11Rate &= IEEE80211_RATE_VAL;
61138075f7dSAdrian Chadd ieee80211_node_set_txrate_dot11rate(ni, dot11Rate);
61238075f7dSAdrian Chadd
613b032f27cSSam Leffler amn->amn_rix = rix;
614b032f27cSSam Leffler }
615b032f27cSSam Leffler amn->amn_ticks = ticks;
616b032f27cSSam Leffler } else
617b032f27cSSam Leffler rix = amn->amn_rix;
618b032f27cSSam Leffler return rix;
619b032f27cSSam Leffler }
620b032f27cSSam Leffler
621b6108616SRui Paulo /*
622b6108616SRui Paulo * Update statistics with tx complete status. Ok is non-zero
623b6108616SRui Paulo * if the packet is known to be ACK'd. Retries has the number
624b6108616SRui Paulo * retransmissions (i.e. xmit attempts - 1).
625b6108616SRui Paulo */
626b6108616SRui Paulo static void
amrr_tx_complete(const struct ieee80211_node * ni,const struct ieee80211_ratectl_tx_status * status)627f6930becSAndriy Voskoboinyk amrr_tx_complete(const struct ieee80211_node *ni,
628f6930becSAndriy Voskoboinyk const struct ieee80211_ratectl_tx_status *status)
629b6108616SRui Paulo {
630b6108616SRui Paulo struct ieee80211_amrr_node *amn = ni->ni_rctls;
631f6930becSAndriy Voskoboinyk int retries;
632f6930becSAndriy Voskoboinyk
6334367c2d1SAndriy Voskoboinyk if (!amn)
6344367c2d1SAndriy Voskoboinyk return;
6354367c2d1SAndriy Voskoboinyk
636f6930becSAndriy Voskoboinyk retries = 0;
637f6930becSAndriy Voskoboinyk if (status->flags & IEEE80211_RATECTL_STATUS_LONG_RETRY)
638f6930becSAndriy Voskoboinyk retries = status->long_retries;
639b6108616SRui Paulo
640b6108616SRui Paulo amn->amn_txcnt++;
641f6930becSAndriy Voskoboinyk if (status->status == IEEE80211_RATECTL_TX_SUCCESS)
642b6108616SRui Paulo amn->amn_success++;
643b6108616SRui Paulo amn->amn_retrycnt += retries;
644b6108616SRui Paulo }
645b6108616SRui Paulo
646f6930becSAndriy Voskoboinyk static void
amrr_tx_update_cb(void * arg,struct ieee80211_node * ni)647f6930becSAndriy Voskoboinyk amrr_tx_update_cb(void *arg, struct ieee80211_node *ni)
648f6930becSAndriy Voskoboinyk {
649f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats = arg;
650f6930becSAndriy Voskoboinyk struct ieee80211_amrr_node *amn = ni->ni_rctls;
651f6930becSAndriy Voskoboinyk int txcnt, success, retrycnt;
652f6930becSAndriy Voskoboinyk
6534367c2d1SAndriy Voskoboinyk if (!amn)
6544367c2d1SAndriy Voskoboinyk return;
6554367c2d1SAndriy Voskoboinyk
656f6930becSAndriy Voskoboinyk txcnt = stats->nframes;
657f6930becSAndriy Voskoboinyk success = stats->nsuccess;
658f6930becSAndriy Voskoboinyk retrycnt = 0;
659f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_RETRIES)
660f6930becSAndriy Voskoboinyk retrycnt = stats->nretries;
661f6930becSAndriy Voskoboinyk
662f6930becSAndriy Voskoboinyk amn->amn_txcnt += txcnt;
663f6930becSAndriy Voskoboinyk amn->amn_success += success;
664f6930becSAndriy Voskoboinyk amn->amn_retrycnt += retrycnt;
665f6930becSAndriy Voskoboinyk }
666f6930becSAndriy Voskoboinyk
667b6108616SRui Paulo /*
668b6108616SRui Paulo * Set tx count/retry statistics explicitly. Intended for
669b6108616SRui Paulo * drivers that poll the device for statistics maintained
670b6108616SRui Paulo * in the device.
671b6108616SRui Paulo */
672b6108616SRui Paulo static void
amrr_tx_update(struct ieee80211vap * vap,struct ieee80211_ratectl_tx_stats * stats)673f6930becSAndriy Voskoboinyk amrr_tx_update(struct ieee80211vap *vap,
674f6930becSAndriy Voskoboinyk struct ieee80211_ratectl_tx_stats *stats)
675b6108616SRui Paulo {
676b6108616SRui Paulo
677f6930becSAndriy Voskoboinyk if (stats->flags & IEEE80211_RATECTL_TX_STATS_NODE)
678f6930becSAndriy Voskoboinyk amrr_tx_update_cb(stats, stats->ni);
679f6930becSAndriy Voskoboinyk else {
680f6930becSAndriy Voskoboinyk ieee80211_iterate_nodes_vap(&vap->iv_ic->ic_sta, vap,
681f6930becSAndriy Voskoboinyk amrr_tx_update_cb, stats);
682f6930becSAndriy Voskoboinyk }
683b6108616SRui Paulo }
684b6108616SRui Paulo
685b032f27cSSam Leffler static int
amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)686b032f27cSSam Leffler amrr_sysctl_interval(SYSCTL_HANDLER_ARGS)
687b032f27cSSam Leffler {
688b6108616SRui Paulo struct ieee80211vap *vap = arg1;
689b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
6904367c2d1SAndriy Voskoboinyk int msecs, error;
691b032f27cSSam Leffler
6924367c2d1SAndriy Voskoboinyk if (!amrr)
6934367c2d1SAndriy Voskoboinyk return ENOMEM;
6944367c2d1SAndriy Voskoboinyk
6954367c2d1SAndriy Voskoboinyk msecs = ticks_to_msecs(amrr->amrr_interval);
696b032f27cSSam Leffler error = sysctl_handle_int(oidp, &msecs, 0, req);
697b032f27cSSam Leffler if (error || !req->newptr)
698b032f27cSSam Leffler return error;
699b6108616SRui Paulo amrr_setinterval(vap, msecs);
700b032f27cSSam Leffler return 0;
701b032f27cSSam Leffler }
702b032f27cSSam Leffler
703b032f27cSSam Leffler static void
amrr_sysctlattach(struct ieee80211vap * vap,struct sysctl_ctx_list * ctx,struct sysctl_oid * tree)704b6108616SRui Paulo amrr_sysctlattach(struct ieee80211vap *vap,
705b032f27cSSam Leffler struct sysctl_ctx_list *ctx, struct sysctl_oid *tree)
706b032f27cSSam Leffler {
707b6108616SRui Paulo struct ieee80211_amrr *amrr = vap->iv_rs;
708b032f27cSSam Leffler
7094367c2d1SAndriy Voskoboinyk if (!amrr)
7104367c2d1SAndriy Voskoboinyk return;
7114367c2d1SAndriy Voskoboinyk
712b032f27cSSam Leffler SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
71308f5e6bbSPawel Biernacki "amrr_rate_interval", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
71408f5e6bbSPawel Biernacki vap, 0, amrr_sysctl_interval, "I", "amrr operation interval (ms)");
715b032f27cSSam Leffler /* XXX bounds check values */
716f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
717b032f27cSSam Leffler "amrr_max_sucess_threshold", CTLFLAG_RW,
718b032f27cSSam Leffler &amrr->amrr_max_success_threshold, 0, "");
719f88910cdSMatthew D Fleming SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
720b032f27cSSam Leffler "amrr_min_sucess_threshold", CTLFLAG_RW,
721b032f27cSSam Leffler &amrr->amrr_min_success_threshold, 0, "");
72274828f25SSam Leffler }
72373931706SAdrian Chadd
72473931706SAdrian Chadd static void
amrr_print_node_rate(struct ieee80211_amrr_node * amn,struct ieee80211_node * ni,struct sbuf * s)725c1475a19SConrad Meyer amrr_print_node_rate(struct ieee80211_amrr_node *amn,
726c1475a19SConrad Meyer struct ieee80211_node *ni, struct sbuf *s)
72773931706SAdrian Chadd {
72873931706SAdrian Chadd int rate;
72973931706SAdrian Chadd struct ieee80211_rateset *rs;
73073931706SAdrian Chadd
7312014462dSAdrian Chadd if (ieee80211_ht_check_tx_ht(ni)) {
73273931706SAdrian Chadd rs = (struct ieee80211_rateset *) &ni->ni_htrates;
73373931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
73473931706SAdrian Chadd sbuf_printf(s, "rate: MCS %d\n", rate);
73573931706SAdrian Chadd } else {
73673931706SAdrian Chadd rs = &ni->ni_rates;
73773931706SAdrian Chadd rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL;
73873931706SAdrian Chadd sbuf_printf(s, "rate: %d Mbit\n", rate / 2);
73973931706SAdrian Chadd }
740c1475a19SConrad Meyer }
74173931706SAdrian Chadd
742c1475a19SConrad Meyer static void
amrr_node_stats(struct ieee80211_node * ni,struct sbuf * s)743c1475a19SConrad Meyer amrr_node_stats(struct ieee80211_node *ni, struct sbuf *s)
744c1475a19SConrad Meyer {
745c1475a19SConrad Meyer struct ieee80211_amrr_node *amn = ni->ni_rctls;
746c1475a19SConrad Meyer
747c1475a19SConrad Meyer /* XXX TODO: check locking? */
748c1475a19SConrad Meyer
749c1475a19SConrad Meyer if (!amn)
750c1475a19SConrad Meyer return;
751c1475a19SConrad Meyer
752c1475a19SConrad Meyer amrr_print_node_rate(amn, ni, s);
75373931706SAdrian Chadd sbuf_printf(s, "ticks: %d\n", amn->amn_ticks);
75473931706SAdrian Chadd sbuf_printf(s, "txcnt: %u\n", amn->amn_txcnt);
75573931706SAdrian Chadd sbuf_printf(s, "success: %u\n", amn->amn_success);
75673931706SAdrian Chadd sbuf_printf(s, "success_threshold: %u\n", amn->amn_success_threshold);
75773931706SAdrian Chadd sbuf_printf(s, "recovery: %u\n", amn->amn_recovery);
75873931706SAdrian Chadd sbuf_printf(s, "retry_cnt: %u\n", amn->amn_retrycnt);
75973931706SAdrian Chadd }
760