Rename printf -> kprintf in sys/ and add some defines where necessary
[dragonfly.git] / sys / netproto / 802_11 / wlan_ratectl / amrr / ieee80211_ratectl_amrr.c
1 /*
2  * Copyright (c) 2004 INRIA
3  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer,
11  *    without modification.
12  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14  *    redistribution must be conditioned upon including a substantially
15  *    similar Disclaimer requirement for further binary redistribution.
16  * 3. Neither the names of the above-listed copyright holders nor the names
17  *    of any contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * Alternatively, this software may be distributed under the terms of the
21  * GNU General Public License ("GPL") version 2 as published by the Free
22  * Software Foundation.
23  *
24  * NO WARRANTY
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
28  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
29  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
30  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
33  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35  * THE POSSIBILITY OF SUCH DAMAGES.
36  *
37  * $FreeBSD: src/sys/dev/ath/ath_rate/amrr/amrr.c,v 1.8.2.3 2006/02/24 19:51:11 sam Exp $
38  * $DragonFly: src/sys/netproto/802_11/wlan_ratectl/amrr/ieee80211_ratectl_amrr.c,v 1.6 2006/12/22 23:57:53 swildner Exp $
39  */
40
41 /*
42  * AMRR rate control. See:
43  * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
44  * "IEEE 802.11 Rate Adaptation: A Practical Approach" by
45  *    Mathieu Lacage, Hossein Manshaei, Thierry Turletti
46  */
47
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/module.h>
52 #include <sys/sysctl.h>
53 #include <sys/serialize.h>
54
55 #include <net/if.h>
56 #include <net/if_media.h>
57 #include <net/if_arp.h>
58
59 #include <netproto/802_11/ieee80211_var.h>
60 #include <netproto/802_11/wlan_ratectl/amrr/ieee80211_amrr_param.h>
61 #include <netproto/802_11/wlan_ratectl/amrr/ieee80211_ratectl_amrr.h>
62
63 #define AMRR_DEBUG
64 #ifdef AMRR_DEBUG
65 #define DPRINTF(asc, lv, fmt, ...) do {         \
66         if ((asc)->debug >= lv)                 \
67                 kprintf(fmt, __VA_ARGS__);      \
68 } while (0)
69 #else
70 #define DPRINTF(asc, lv, fmt, ...)
71 #endif
72
73 #define AMRR_REQUIRE_STATS1     (IEEE80211_RATECTL_STATS_RES |          \
74                                  IEEE80211_RATECTL_STATS_PKT_NORETRY)
75 #define AMRR_REQUIRE_STATS2     (IEEE80211_RATECTL_STATS_PKT_NORETRY |  \
76                                  IEEE80211_RATECTL_STATS_PKT_OK |       \
77                                  IEEE80211_RATECTL_STATS_PKT_ERR |      \
78                                  IEEE80211_RATECTL_STATS_RETRIES)
79 #define AMRR_MEET_REQUIRE_STATS1(stats_mask)    \
80         (((stats_mask) & AMRR_REQUIRE_STATS1) == AMRR_REQUIRE_STATS1)
81 #define AMRR_MEET_REQUIRE_STATS2(stats_mask)    \
82         (((stats_mask) & AMRR_REQUIRE_STATS2) == AMRR_REQUIRE_STATS2)
83
84 static void     *amrr_attach(struct ieee80211com *);
85 static void     amrr_detach(void *);
86 static void     amrr_data_alloc(struct ieee80211_node *);
87 static void     amrr_data_free(struct ieee80211_node *);
88 static void     amrr_data_dup(const struct ieee80211_node *,
89                               struct ieee80211_node *);
90 static void     amrr_newstate(void *, enum ieee80211_state);
91 static void     amrr_tx_complete(void *, struct ieee80211_node *, int,
92                                  const struct ieee80211_ratectl_res[],
93                                  int, int, int, int);
94 static void     amrr_newassoc(void *, struct ieee80211_node *, int);
95 static int      amrr_findrate(void *, struct ieee80211_node *, int,
96                               int[], int);
97
98 static void     amrr_sysctl_attach(struct amrr_softc *);
99 static void     amrr_update(struct amrr_softc *, struct ieee80211_node *, int);
100 static void     amrr_start(struct amrr_softc *, struct ieee80211_node *);
101 static void     amrr_tick(void *);
102 static void     amrr_ratectl(void *, struct ieee80211_node *);
103 static void     amrr_gather_stats(struct amrr_softc *, struct ieee80211_node *);
104
105 static const struct ieee80211_ratectl amrr = {
106         .rc_name        = "amrr",
107         .rc_ratectl     = IEEE80211_RATECTL_AMRR,
108         .rc_attach      = amrr_attach,
109         .rc_detach      = amrr_detach,
110         .rc_data_alloc  = amrr_data_alloc,
111         .rc_data_free   = amrr_data_free,
112         .rc_data_dup    = amrr_data_dup,
113         .rc_newstate    = amrr_newstate,
114         .rc_tx_complete = amrr_tx_complete,
115         .rc_newassoc    = amrr_newassoc,
116         .rc_findrate    = amrr_findrate
117 };
118
119 static u_int    amrr_nrefs;
120
121 MALLOC_DEFINE(M_AMRR_RATECTL_DATA, "amrr_ratectl_data",
122               "amrr rate control data");
123
124 static int
125 amrr_findrate(void *arg __unused, struct ieee80211_node *ni,
126               int frame_len __unused, int rateidx[], int rateidx_len)
127 {
128         int i, rate_idx = ni->ni_txrate;
129
130         for (i = 0; i < rateidx_len; ++i) {
131                 if (rate_idx < 0)
132                         break;
133                 rateidx[i] = rate_idx--;
134         }
135         if (rateidx_len > 1)
136                 rateidx[rateidx_len - 1] = 0;
137         return i;
138 }
139
140 static void
141 amrr_tx_complete(void *arg __unused, struct ieee80211_node *ni,
142                  int frame_len __unused,
143                  const struct ieee80211_ratectl_res res[],
144                  int res_len, int short_retries __unused,
145                  int long_retries __unused, int is_fail)
146 {
147         struct amrr_data *ad = ni->ni_rate_data;
148         u_int total_tries;
149         int i;
150
151         if (ad == NULL)
152                 return;
153
154         total_tries = 0;
155         for (i = 0; i < res_len; ++i)
156                 total_tries += res[i].rc_res_tries;
157         ad->ad_tx_cnt += total_tries;
158
159         ad->ad_tx_failure_cnt += total_tries;
160         if (res_len == 1 && !is_fail) {
161                 KKASSERT(ad->ad_tx_failure_cnt != 0);
162                 /* One packet is successfully transmitted at desired rate */
163                 ad->ad_tx_failure_cnt--;
164         }
165 }
166
167 static void
168 amrr_newassoc(void *arg, struct ieee80211_node *ni, int isnew)
169 {
170         if (isnew)
171                 amrr_start(arg, ni);
172 }
173
174 /*
175  * The code below assumes that we are dealing with hardware multi rate retry
176  * I have no idea what will happen if you try to use this module with another
177  * type of hardware. Your machine might catch fire or it might work with
178  * horrible performance...
179  */
180 static void
181 amrr_update(struct amrr_softc *asc, struct ieee80211_node *ni, int rate)
182 {
183         struct amrr_data *ad = ni->ni_rate_data;
184
185         DPRINTF(asc, 5, "%s: set xmit rate for %6D to %dM\n",
186                 __func__, ni->ni_macaddr, ":",
187                 ni->ni_rates.rs_nrates > 0 ?
188                 IEEE80211_RS_RATE(&ni->ni_rates, rate) / 2 : 0);
189
190         ni->ni_txrate = rate;
191
192         if (ad == NULL) {
193                 amrr_data_alloc(ni);
194                 ad = ni->ni_rate_data;
195                 if (ad == NULL)
196                         return;
197         }
198
199         ad->ad_tx_cnt = 0;
200         ad->ad_tx_failure_cnt = 0;
201         ad->ad_success = 0;
202         ad->ad_recovery = 0;
203         ad->ad_success_threshold = asc->min_success_threshold;
204 }
205
206 /*
207  * Set the starting transmit rate for a node.
208  */
209 static void
210 amrr_start(struct amrr_softc *asc, struct ieee80211_node *ni)
211 {
212 #define RATE(_ix)       IEEE80211_RS_RATE(&ni->ni_rates, (_ix))
213         struct ieee80211com *ic = asc->ic;
214         int srate;
215
216         KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
217
218         if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
219                 /*
220                  * For adhoc or ibss mode, start from the lowest rate.
221                  */
222                 if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
223                     ic->ic_opmode == IEEE80211_M_IBSS) {
224                         amrr_update(asc, ni, 0);
225                         return;
226                 }
227
228                 /*
229                  * No fixed rate is requested. For 11b start with
230                  * the highest negotiated rate; otherwise, for 11g
231                  * and 11a, we start "in the middle" at 24Mb or 36Mb.
232                  */
233                 srate = ni->ni_rates.rs_nrates - 1;
234                 if (ic->ic_curmode != IEEE80211_MODE_11B) {
235                         /*
236                          * Scan the negotiated rate set to find the
237                          * closest rate.
238                          */
239                         /* NB: the rate set is assumed sorted */
240                         for (; srate >= 0 && RATE(srate) > 72; srate--)
241                                 ;
242                         KASSERT(srate >= 0, ("bogus rate set"));
243                 }
244         } else {
245                 /*
246                  * A fixed rate is to be used; ic_fixed_rate is an
247                  * index into the supported rate set.  Convert this
248                  * to the index into the negotiated rate set for
249                  * the node.  We know the rate is there because the
250                  * rate set is checked when the station associates.
251                  */
252                 const struct ieee80211_rateset *rs =
253                         &ic->ic_sup_rates[ic->ic_curmode];
254                 int r = IEEE80211_RS_RATE(rs, ic->ic_fixed_rate);
255
256                 /* NB: the rate set is assumed sorted */
257                 srate = ni->ni_rates.rs_nrates - 1;
258                 for (; srate >= 0 && RATE(srate) != r; srate--)
259                         ;
260                 KASSERT(srate >= 0,
261                         ("fixed rate %d not in rate set", ic->ic_fixed_rate));
262         }
263         amrr_update(asc, ni, srate);
264 #undef RATE
265 }
266
267 static void
268 amrr_rate_cb(void *arg, struct ieee80211_node *ni)
269 {
270         amrr_update(arg, ni, 0);
271 }
272
273 /*
274  * Reset the rate control state for each 802.11 state transition.
275  */
276 static void
277 amrr_newstate(void *arg, enum ieee80211_state state)
278 {
279         struct amrr_softc *asc = arg;
280         struct ieee80211com *ic = asc->ic;
281         struct ieee80211_node *ni;
282
283         if (state == IEEE80211_S_INIT) {
284                 callout_stop(&asc->timer);
285                 return;
286         }
287
288         if (ic->ic_opmode == IEEE80211_M_STA) {
289                 /*
290                  * Reset local xmit state; this is really only
291                  * meaningful when operating in station mode.
292                  */
293                 ni = ic->ic_bss;
294                 if (state == IEEE80211_S_RUN)
295                         amrr_start(asc, ni);
296                 else
297                         amrr_update(asc, ni, 0);
298         } else {
299                 /*
300                  * When operating as a station the node table holds
301                  * the AP's that were discovered during scanning.
302                  * For any other operating mode we want to reset the
303                  * tx rate state of each node.
304                  */
305                 ieee80211_iterate_nodes(&ic->ic_sta, amrr_rate_cb, asc);
306                 amrr_update(asc, ic->ic_bss, 0);
307         }
308         if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE &&
309             state == IEEE80211_S_RUN) {
310                 int interval;
311
312                 /*
313                  * Start the background rate control thread if we
314                  * are not configured to use a fixed xmit rate.
315                  */
316                 interval = asc->interval;
317                 if (ic->ic_opmode == IEEE80211_M_STA)
318                         interval /= 2;
319                 callout_reset(&asc->timer, (interval * hz) / 1000,
320                               amrr_tick, asc);
321         }
322 }
323
324 static void
325 amrr_gather_stats(struct amrr_softc *asc, struct ieee80211_node *ni)
326 {
327         struct ieee80211com *ic = asc->ic;
328         const struct ieee80211_ratectl_state *st = &ic->ic_ratectl;
329         struct amrr_data *ad = ni->ni_rate_data;
330         struct ieee80211_ratectl_stats stats;
331         u_int total_tries = 0;
332
333         st->rc_st_stats(ic, ni, &stats);
334
335         if (AMRR_MEET_REQUIRE_STATS1(st->rc_st_valid_stats)) {
336                 int i;
337
338                 for (i = 0; i < stats.stats_res_len; ++i)
339                         total_tries += stats.stats_res[i].rc_res_tries;
340         } else if (AMRR_MEET_REQUIRE_STATS2(st->rc_st_valid_stats)) {
341                 total_tries = stats.stats_pkt_ok +
342                               stats.stats_pkt_err +
343                               stats.stats_short_retries +
344                               stats.stats_long_retries;
345         }
346
347         ad->ad_tx_cnt += total_tries;
348         ad->ad_tx_failure_cnt += (total_tries - stats.stats_pkt_noretry);
349 }
350
351 /* 
352  * Examine and potentially adjust the transmit rate.
353  */
354 static void
355 amrr_ratectl(void *arg, struct ieee80211_node *ni)
356 {
357         struct amrr_softc *asc = arg;
358         const struct ieee80211_ratectl_state *st = &asc->ic->ic_ratectl;
359         struct amrr_data *ad = ni->ni_rate_data;
360         int old_rate;
361
362         if (ad == NULL) {
363                 /* We are not ready to go, set TX rate to lowest one */
364                 ni->ni_txrate = 0;
365                 return;
366         }
367
368 #define is_success(ad)  (ad->ad_tx_failure_cnt < (ad->ad_tx_cnt / 10))
369 #define is_enough(ad)   (ad->ad_tx_cnt > 10)
370 #define is_failure(ad)  (ad->ad_tx_failure_cnt > (ad->ad_tx_cnt / 3))
371 #define is_max_rate(ni) ((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)
372 #define is_min_rate(ni) (ni->ni_txrate == 0)
373
374         old_rate = ni->ni_txrate;
375
376         if (st->rc_st_stats != NULL) {
377                 if (!AMRR_MEET_REQUIRE_STATS1(st->rc_st_valid_stats) &&
378                     !AMRR_MEET_REQUIRE_STATS2(st->rc_st_valid_stats))
379                         return;
380                 amrr_gather_stats(asc, ni);
381         }
382   
383         DPRINTF(asc, 10, "tx_cnt: %u tx_failure_cnt: %u -- "
384                 "threshold: %d\n",
385                 ad->ad_tx_cnt, ad->ad_tx_failure_cnt,
386                 ad->ad_success_threshold);
387
388         if (is_success(ad) && is_enough(ad)) {
389                 ad->ad_success++;
390                 if (ad->ad_success == ad->ad_success_threshold &&
391                     !is_max_rate(ni)) {
392                         ad->ad_recovery = 1;
393                         ad->ad_success = 0;
394                         ni->ni_txrate++;
395                         DPRINTF(asc, 5, "increase rate to %d\n", ni->ni_txrate);
396                 } else {
397                         ad->ad_recovery = 0;
398                 }
399         } else if (is_failure(ad)) {
400                 ad->ad_success = 0;
401                 if (!is_min_rate(ni)) {
402                         if (ad->ad_recovery) {
403                                 /* recovery failure. */
404                                 ad->ad_success_threshold *= 2;
405                                 ad->ad_success_threshold =
406                                         min(ad->ad_success_threshold,
407                                             (u_int)asc->max_success_threshold);
408                                 DPRINTF(asc, 5, "decrease rate recovery thr: "
409                                         "%d\n", ad->ad_success_threshold);
410                         } else {
411                                 /* simple failure. */
412                                 ad->ad_success_threshold =
413                                         asc->min_success_threshold;
414                                 DPRINTF(asc, 5, "decrease rate normal thr: "
415                                         "%d\n", ad->ad_success_threshold);
416                         }
417                         ad->ad_recovery = 0;
418                         ni->ni_txrate--;
419                 } else {
420                         ad->ad_recovery = 0;
421                 }
422         }
423         if (is_enough(ad) || old_rate != ni->ni_txrate) {
424                 /* reset counters. */
425                 ad->ad_tx_cnt = 0;
426                 ad->ad_tx_failure_cnt = 0;
427         }
428         if (old_rate != ni->ni_txrate)
429                 amrr_update(asc, ni, ni->ni_txrate);
430 }
431
432 static void
433 amrr_tick(void *arg)
434 {
435         struct amrr_softc *asc = arg;
436         struct ieee80211com *ic = asc->ic;
437         struct ifnet *ifp = &ic->ic_if;
438         int interval;
439
440         lwkt_serialize_enter(ifp->if_serializer);
441
442         if (ifp->if_flags & IFF_RUNNING) {
443                 if (ic->ic_opmode == IEEE80211_M_STA)
444                         amrr_ratectl(asc, ic->ic_bss);  /* NB: no reference */
445                 else
446                         ieee80211_iterate_nodes(&ic->ic_sta, amrr_ratectl, asc);
447         }
448         interval = asc->interval;
449         if (ic->ic_opmode == IEEE80211_M_STA)
450                 interval /= 2;
451         callout_reset(&asc->timer, (interval * hz) / 1000, amrr_tick, asc);
452
453         lwkt_serialize_exit(ifp->if_serializer);
454 }
455
456 static void
457 amrr_sysctl_attach(struct amrr_softc *asc)
458 {
459         struct ieee80211com *ic = asc->ic;
460         struct ieee80211_amrr_param *param;
461
462         param = ic->ic_ratectl.rc_st_param;
463         if (param != NULL) {
464                 asc->interval = param->amrr_interval;
465                 asc->max_success_threshold = param->amrr_max_success_threshold;
466                 asc->min_success_threshold = param->amrr_min_success_threshold;
467         } else {
468                 asc->interval = IEEE80211_AMRR_INTERVAL;
469                 asc->max_success_threshold = IEEE80211_AMRR_MAX_SUCCESS_THR;
470                 asc->min_success_threshold = IEEE80211_AMRR_MIN_SUCCESS_THR;
471         }
472         asc->debug = 0;
473
474         sysctl_ctx_init(&asc->sysctl_ctx);
475         asc->sysctl_oid = SYSCTL_ADD_NODE(&asc->sysctl_ctx,
476                 SYSCTL_CHILDREN(ic->ic_sysctl_oid),
477                 OID_AUTO, "amrr_ratectl", CTLFLAG_RD, 0, "");
478         if (asc->sysctl_oid == NULL) {
479                 kprintf("wlan_ratectl_amrr: create sysctl tree failed\n");
480                 return;
481         }
482
483         SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
484                        OID_AUTO, "interval", CTLFLAG_RW,
485                        &asc->interval, 0,
486                        "rate control: operation interval (ms)");
487
488         /* XXX bounds check values */
489         SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
490                        OID_AUTO, "max_sucess_threshold", CTLFLAG_RW,
491                        &asc->max_success_threshold, 0, "");
492
493         SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
494                        OID_AUTO, "min_sucess_threshold", CTLFLAG_RW,
495                        &asc->min_success_threshold, 0, "");
496
497         SYSCTL_ADD_INT(&asc->sysctl_ctx, SYSCTL_CHILDREN(asc->sysctl_oid),
498                        OID_AUTO, "debug", CTLFLAG_RW,
499                        &asc->debug, 0, "debug level");
500 }
501
502 static void *
503 amrr_attach(struct ieee80211com *ic)
504 {
505         const struct ieee80211_ratectl_state *st = &ic->ic_ratectl;
506         struct amrr_softc *asc;
507
508         if (st->rc_st_stats != NULL &&
509             !AMRR_MEET_REQUIRE_STATS1(st->rc_st_valid_stats) &&
510             !AMRR_MEET_REQUIRE_STATS2(st->rc_st_valid_stats)) {
511                 if_printf(&ic->ic_if, "WARNING: %s needs more average "
512                           "statistics to work properly\n", amrr.rc_name);
513         }
514
515         amrr_nrefs++;
516
517         asc = kmalloc(sizeof(struct amrr_softc), M_DEVBUF, M_WAITOK | M_ZERO);
518
519         asc->ic = ic;
520         callout_init(&asc->timer);
521         amrr_sysctl_attach(asc);
522
523         amrr_newstate(asc, ic->ic_state);
524
525         return asc;
526 }
527
528 static void
529 _amrr_data_free(void *arg __unused, struct ieee80211_node *ni)
530 {
531         amrr_data_free(ni);
532 }
533
534 void
535 amrr_detach(void *arg)
536 {
537         struct amrr_softc *asc = arg;
538         struct ieee80211com *ic = asc->ic;
539
540         amrr_newstate(asc, IEEE80211_S_INIT);
541
542         ieee80211_iterate_nodes(&ic->ic_sta, _amrr_data_free, NULL);
543         ieee80211_iterate_nodes(&ic->ic_scan, _amrr_data_free, NULL);
544
545         if (asc->sysctl_oid != NULL)
546                 sysctl_ctx_free(&asc->sysctl_ctx);
547         kfree(asc, M_DEVBUF);
548
549         amrr_nrefs--;
550 }
551
552 static void
553 amrr_data_free(struct ieee80211_node *ni)
554 {
555         if (ni->ni_rate_data != NULL) {
556                 kfree(ni->ni_rate_data, M_AMRR_RATECTL_DATA);
557                 ni->ni_rate_data = NULL;
558         }
559 }
560
561 static void
562 amrr_data_alloc(struct ieee80211_node *ni)
563 {
564         KKASSERT(ni->ni_rate_data == NULL);
565         ni->ni_rate_data = kmalloc(sizeof(struct amrr_data),
566                                   M_AMRR_RATECTL_DATA, M_NOWAIT | M_ZERO);
567 }
568
569 static void
570 amrr_data_dup(const struct ieee80211_node *oni, struct ieee80211_node *nni)
571 {
572         if (oni->ni_rate_data == NULL || nni->ni_rate_data == NULL)
573                 return;
574
575         bcopy(oni->ni_rate_data, nni->ni_rate_data, sizeof(struct amrr_data));
576 }
577
578 /*
579  * Module glue.
580  */
581 static int
582 amrr_modevent(module_t mod, int type, void *unused)
583 {
584         switch (type) {
585         case MOD_LOAD:
586                 ieee80211_ratectl_register(&amrr);
587                 return 0;
588         case MOD_UNLOAD:
589                 if (amrr_nrefs) {
590                         kprintf("wlan_ratectl_amrr: still in use "
591                                "(%u dynamic refs)\n", amrr_nrefs);
592                         return EBUSY;
593                 }
594                 ieee80211_ratectl_unregister(&amrr);
595                 return 0;
596         }
597         return EINVAL;
598 }
599
600 static moduledata_t amrr_mod = {
601         "wlan_ratectl_amrr",
602         amrr_modevent,
603         0
604 };
605 DECLARE_MODULE(wlan_ratectl_amrr, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
606 MODULE_VERSION(wlan_ratectl_amrr, 1);
607 MODULE_DEPEND(wlan_ratectl_amrr, wlan, 1, 1, 1);