Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[wrt350n-kernel.git] / net / mac80211 / rc80211_pid_algo.c
blobec5db237cd2ec63b198ca396e1b9ad04c4afa249
1 /*
2 * Copyright 2002-2005, Instant802 Networks, Inc.
3 * Copyright 2005, Devicescape Software, Inc.
4 * Copyright 2007, Mattias Nissler <mattias.nissler@gmx.de>
5 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
6 * Copyright 2007, Stefano Brivio <stefano.brivio@polimi.it>
7 =======
8 * Copyright 2007-2008, Stefano Brivio <stefano.brivio@polimi.it>
9 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
16 #include <linux/netdevice.h>
17 #include <linux/types.h>
18 #include <linux/skbuff.h>
19 #include <linux/debugfs.h>
20 #include <net/mac80211.h>
21 #include "ieee80211_rate.h"
23 #include "rc80211_pid.h"
26 /* This is an implementation of a TX rate control algorithm that uses a PID
27 * controller. Given a target failed frames rate, the controller decides about
28 * TX rate changes to meet the target failed frames rate.
30 * The controller basically computes the following:
32 * adj = CP * err + CI * err_avg + CD * (err - last_err) * (1 + sharpening)
34 * where
35 * adj adjustment value that is used to switch TX rate (see below)
36 * err current error: target vs. current failed frames percentage
37 * last_err last error
38 * err_avg average (i.e. poor man's integral) of recent errors
39 * sharpening non-zero when fast response is needed (i.e. right after
40 * association or no frames sent for a long time), heading
41 * to zero over time
42 * CP Proportional coefficient
43 * CI Integral coefficient
44 * CD Derivative coefficient
46 * CP, CI, CD are subject to careful tuning.
48 * The integral component uses a exponential moving average approach instead of
49 * an actual sliding window. The advantage is that we don't need to keep an
50 * array of the last N error values and computation is easier.
52 * Once we have the adj value, we map it to a rate by means of a learning
53 * algorithm. This algorithm keeps the state of the percentual failed frames
54 * difference between rates. The behaviour of the lowest available rate is kept
55 * as a reference value, and every time we switch between two rates, we compute
56 * the difference between the failed frames each rate exhibited. By doing so,
57 * we compare behaviours which different rates exhibited in adjacent timeslices,
58 * thus the comparison is minimally affected by external conditions. This
59 * difference gets propagated to the whole set of measurements, so that the
60 * reference is always the same. Periodically, we normalize this set so that
61 * recent events weigh the most. By comparing the adj value with this set, we
62 * avoid pejorative switches to lower rates and allow for switches to higher
63 * rates if they behaved well.
65 * Note that for the computations we use a fixed-point representation to avoid
66 * floating point arithmetic. Hence, all values are shifted left by
67 * RC_PID_ARITH_SHIFT.
70 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
72 /* Shift the adjustment so that we won't switch to a lower rate if it exhibited
73 * a worse failed frames behaviour and we'll choose the highest rate whose
74 * failed frames behaviour is not worse than the one of the original rate
75 * target. While at it, check that the adjustment is within the ranges. Then,
76 * provide the new rate index. */
77 static int rate_control_pid_shift_adjust(struct rc_pid_rateinfo *r,
78 int adj, int cur, int l)
80 int i, j, k, tmp;
82 j = r[cur].rev_index;
83 i = j + adj;
85 if (i < 0)
86 return r[0].index;
87 if (i >= l - 1)
88 return r[l - 1].index;
90 tmp = i;
92 if (adj < 0) {
93 for (k = j; k >= i; k--)
94 if (r[k].diff <= r[j].diff)
95 tmp = k;
96 } else {
97 for (k = i + 1; k + i < l; k++)
98 if (r[k].diff <= r[i].diff)
99 tmp = k;
102 return r[tmp].index;
105 =======
106 /* Adjust the rate while ensuring that we won't switch to a lower rate if it
107 * exhibited a worse failed frames behaviour and we'll choose the highest rate
108 * whose failed frames behaviour is not worse than the one of the original rate
109 * target. While at it, check that the new rate is valid. */
110 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
111 static void rate_control_pid_adjust_rate(struct ieee80211_local *local,
112 struct sta_info *sta, int adj,
113 struct rc_pid_rateinfo *rinfo)
115 struct ieee80211_sub_if_data *sdata;
116 struct ieee80211_hw_mode *mode;
117 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
118 int newidx;
119 int maxrate;
120 int back = (adj > 0) ? 1 : -1;
121 =======
122 int cur_sorted, new_sorted, probe, tmp, n_bitrates;
123 int cur = sta->txrate;
124 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
126 sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
128 mode = local->oper_hw_mode;
129 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
130 maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1;
131 =======
132 n_bitrates = mode->num_rates;
133 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
135 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
136 newidx = rate_control_pid_shift_adjust(rinfo, adj, sta->txrate,
137 mode->num_rates);
138 =======
139 /* Map passed arguments to sorted values. */
140 cur_sorted = rinfo[cur].rev_index;
141 new_sorted = cur_sorted + adj;
142 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
144 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
145 while (newidx != sta->txrate) {
146 if (rate_supported(sta, mode, newidx) &&
147 (maxrate < 0 || newidx <= maxrate)) {
148 sta->txrate = newidx;
149 break;
151 =======
152 /* Check limits. */
153 if (new_sorted < 0)
154 new_sorted = rinfo[0].rev_index;
155 else if (new_sorted >= n_bitrates)
156 new_sorted = rinfo[n_bitrates - 1].rev_index;
157 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
159 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
160 newidx += back;
161 =======
162 tmp = new_sorted;
164 if (adj < 0) {
165 /* Ensure that the rate decrease isn't disadvantageous. */
166 for (probe = cur_sorted; probe >= new_sorted; probe--)
167 if (rinfo[probe].diff <= rinfo[cur_sorted].diff &&
168 rate_supported(sta, mode, rinfo[probe].index))
169 tmp = probe;
170 } else {
171 /* Look for rate increase with zero (or below) cost. */
172 for (probe = new_sorted + 1; probe < n_bitrates; probe++)
173 if (rinfo[probe].diff <= rinfo[new_sorted].diff &&
174 rate_supported(sta, mode, rinfo[probe].index))
175 tmp = probe;
176 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
179 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
180 =======
181 /* Fit the rate found to the nearest supported rate. */
182 do {
183 if (rate_supported(sta, mode, rinfo[tmp].index)) {
184 sta->txrate = rinfo[tmp].index;
185 break;
187 if (adj < 0)
188 tmp--;
189 else
190 tmp++;
191 } while (tmp < n_bitrates && tmp >= 0);
193 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
194 #ifdef CONFIG_MAC80211_DEBUGFS
195 rate_control_pid_event_rate_change(
196 &((struct rc_pid_sta_info *)sta->rate_ctrl_priv)->events,
197 <<<<<<< HEAD:net/mac80211/rc80211_pid_algo.c
198 newidx, mode->rates[newidx].rate);
199 =======
200 cur, mode->rates[cur].rate);
201 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a:net/mac80211/rc80211_pid_algo.c
202 #endif
205 /* Normalize the failed frames per-rate differences. */
206 static void rate_control_pid_normalize(struct rc_pid_info *pinfo, int l)
208 int i, norm_offset = pinfo->norm_offset;
209 struct rc_pid_rateinfo *r = pinfo->rinfo;
211 if (r[0].diff > norm_offset)
212 r[0].diff -= norm_offset;
213 else if (r[0].diff < -norm_offset)
214 r[0].diff += norm_offset;
215 for (i = 0; i < l - 1; i++)
216 if (r[i + 1].diff > r[i].diff + norm_offset)
217 r[i + 1].diff -= norm_offset;
218 else if (r[i + 1].diff <= r[i].diff)
219 r[i + 1].diff += norm_offset;
222 static void rate_control_pid_sample(struct rc_pid_info *pinfo,
223 struct ieee80211_local *local,
224 struct sta_info *sta)
226 struct rc_pid_sta_info *spinfo = sta->rate_ctrl_priv;
227 struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
228 struct ieee80211_hw_mode *mode;
229 u32 pf;
230 s32 err_avg;
231 u32 err_prop;
232 u32 err_int;
233 u32 err_der;
234 int adj, i, j, tmp;
235 unsigned long period;
237 mode = local->oper_hw_mode;
238 spinfo = sta->rate_ctrl_priv;
240 /* In case nothing happened during the previous control interval, turn
241 * the sharpening factor on. */
242 period = (HZ * pinfo->sampling_period + 500) / 1000;
243 if (!period)
244 period = 1;
245 if (jiffies - spinfo->last_sample > 2 * period)
246 spinfo->sharp_cnt = pinfo->sharpen_duration;
248 spinfo->last_sample = jiffies;
250 /* This should never happen, but in case, we assume the old sample is
251 * still a good measurement and copy it. */
252 if (unlikely(spinfo->tx_num_xmit == 0))
253 pf = spinfo->last_pf;
254 else {
255 pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
256 pf <<= RC_PID_ARITH_SHIFT;
259 spinfo->tx_num_xmit = 0;
260 spinfo->tx_num_failed = 0;
262 /* If we just switched rate, update the rate behaviour info. */
263 if (pinfo->oldrate != sta->txrate) {
265 i = rinfo[pinfo->oldrate].rev_index;
266 j = rinfo[sta->txrate].rev_index;
268 tmp = (pf - spinfo->last_pf);
269 tmp = RC_PID_DO_ARITH_RIGHT_SHIFT(tmp, RC_PID_ARITH_SHIFT);
271 rinfo[j].diff = rinfo[i].diff + tmp;
272 pinfo->oldrate = sta->txrate;
274 rate_control_pid_normalize(pinfo, mode->num_rates);
276 /* Compute the proportional, integral and derivative errors. */
277 err_prop = (pinfo->target << RC_PID_ARITH_SHIFT) - pf;
279 err_avg = spinfo->err_avg_sc >> pinfo->smoothing_shift;
280 spinfo->err_avg_sc = spinfo->err_avg_sc - err_avg + err_prop;
281 err_int = spinfo->err_avg_sc >> pinfo->smoothing_shift;
283 err_der = (pf - spinfo->last_pf) *
284 (1 + pinfo->sharpen_factor * spinfo->sharp_cnt);
285 spinfo->last_pf = pf;
286 if (spinfo->sharp_cnt)
287 spinfo->sharp_cnt--;
289 #ifdef CONFIG_MAC80211_DEBUGFS
290 rate_control_pid_event_pf_sample(&spinfo->events, pf, err_prop, err_int,
291 err_der);
292 #endif
294 /* Compute the controller output. */
295 adj = (err_prop * pinfo->coeff_p + err_int * pinfo->coeff_i
296 + err_der * pinfo->coeff_d);
297 adj = RC_PID_DO_ARITH_RIGHT_SHIFT(adj, 2 * RC_PID_ARITH_SHIFT);
299 /* Change rate. */
300 if (adj)
301 rate_control_pid_adjust_rate(local, sta, adj, rinfo);
304 static void rate_control_pid_tx_status(void *priv, struct net_device *dev,
305 struct sk_buff *skb,
306 struct ieee80211_tx_status *status)
308 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
309 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
310 struct ieee80211_sub_if_data *sdata;
311 struct rc_pid_info *pinfo = priv;
312 struct sta_info *sta;
313 struct rc_pid_sta_info *spinfo;
314 unsigned long period;
316 sta = sta_info_get(local, hdr->addr1);
318 if (!sta)
319 return;
321 /* Don't update the state if we're not controlling the rate. */
322 sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
323 if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) {
324 sta->txrate = sdata->bss->max_ratectrl_rateidx;
325 return;
328 /* Ignore all frames that were sent with a different rate than the rate
329 * we currently advise mac80211 to use. */
330 if (status->control.rate != &local->oper_hw_mode->rates[sta->txrate])
331 goto ignore;
333 spinfo = sta->rate_ctrl_priv;
334 spinfo->tx_num_xmit++;
336 #ifdef CONFIG_MAC80211_DEBUGFS
337 rate_control_pid_event_tx_status(&spinfo->events, status);
338 #endif
340 /* We count frames that totally failed to be transmitted as two bad
341 * frames, those that made it out but had some retries as one good and
342 * one bad frame. */
343 if (status->excessive_retries) {
344 spinfo->tx_num_failed += 2;
345 spinfo->tx_num_xmit++;
346 } else if (status->retry_count) {
347 spinfo->tx_num_failed++;
348 spinfo->tx_num_xmit++;
351 if (status->excessive_retries) {
352 sta->tx_retry_failed++;
353 sta->tx_num_consecutive_failures++;
354 sta->tx_num_mpdu_fail++;
355 } else {
356 sta->last_ack_rssi[0] = sta->last_ack_rssi[1];
357 sta->last_ack_rssi[1] = sta->last_ack_rssi[2];
358 sta->last_ack_rssi[2] = status->ack_signal;
359 sta->tx_num_consecutive_failures = 0;
360 sta->tx_num_mpdu_ok++;
362 sta->tx_retry_count += status->retry_count;
363 sta->tx_num_mpdu_fail += status->retry_count;
365 /* Update PID controller state. */
366 period = (HZ * pinfo->sampling_period + 500) / 1000;
367 if (!period)
368 period = 1;
369 if (time_after(jiffies, spinfo->last_sample + period))
370 rate_control_pid_sample(pinfo, local, sta);
372 ignore:
373 sta_info_put(sta);
376 static void rate_control_pid_get_rate(void *priv, struct net_device *dev,
377 struct ieee80211_hw_mode *mode,
378 struct sk_buff *skb,
379 struct rate_selection *sel)
381 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
382 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
383 struct ieee80211_sub_if_data *sdata;
384 struct sta_info *sta;
385 int rateidx;
386 u16 fc;
388 sta = sta_info_get(local, hdr->addr1);
390 /* Send management frames and broadcast/multicast data using lowest
391 * rate. */
392 fc = le16_to_cpu(hdr->frame_control);
393 if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
394 is_multicast_ether_addr(hdr->addr1) || !sta) {
395 sel->rate = rate_lowest(local, mode, sta);
396 if (sta)
397 sta_info_put(sta);
398 return;
401 /* If a forced rate is in effect, select it. */
402 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
403 if (sdata->bss && sdata->bss->force_unicast_rateidx > -1)
404 sta->txrate = sdata->bss->force_unicast_rateidx;
406 rateidx = sta->txrate;
408 if (rateidx >= mode->num_rates)
409 rateidx = mode->num_rates - 1;
411 sta->last_txrate = rateidx;
413 sta_info_put(sta);
415 sel->rate = &mode->rates[rateidx];
417 #ifdef CONFIG_MAC80211_DEBUGFS
418 rate_control_pid_event_tx_rate(
419 &((struct rc_pid_sta_info *) sta->rate_ctrl_priv)->events,
420 rateidx, mode->rates[rateidx].rate);
421 #endif
424 static void rate_control_pid_rate_init(void *priv, void *priv_sta,
425 struct ieee80211_local *local,
426 struct sta_info *sta)
428 /* TODO: This routine should consider using RSSI from previous packets
429 * as we need to have IEEE 802.1X auth succeed immediately after assoc..
430 * Until that method is implemented, we will use the lowest supported
431 * rate as a workaround. */
432 sta->txrate = rate_lowest_index(local, local->oper_hw_mode, sta);
435 static void *rate_control_pid_alloc(struct ieee80211_local *local)
437 struct rc_pid_info *pinfo;
438 struct rc_pid_rateinfo *rinfo;
439 struct ieee80211_hw_mode *mode;
440 int i, j, tmp;
441 bool s;
442 #ifdef CONFIG_MAC80211_DEBUGFS
443 struct rc_pid_debugfs_entries *de;
444 #endif
446 pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC);
447 if (!pinfo)
448 return NULL;
450 /* We can safely assume that oper_hw_mode won't change unless we get
451 * reinitialized. */
452 mode = local->oper_hw_mode;
453 rinfo = kmalloc(sizeof(*rinfo) * mode->num_rates, GFP_ATOMIC);
454 if (!rinfo) {
455 kfree(pinfo);
456 return NULL;
459 /* Sort the rates. This is optimized for the most common case (i.e.
460 * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed
461 * mapping too. */
462 for (i = 0; i < mode->num_rates; i++) {
463 rinfo[i].index = i;
464 rinfo[i].rev_index = i;
465 if (pinfo->fast_start)
466 rinfo[i].diff = 0;
467 else
468 rinfo[i].diff = i * pinfo->norm_offset;
470 for (i = 1; i < mode->num_rates; i++) {
471 s = 0;
472 for (j = 0; j < mode->num_rates - i; j++)
473 if (unlikely(mode->rates[rinfo[j].index].rate >
474 mode->rates[rinfo[j + 1].index].rate)) {
475 tmp = rinfo[j].index;
476 rinfo[j].index = rinfo[j + 1].index;
477 rinfo[j + 1].index = tmp;
478 rinfo[rinfo[j].index].rev_index = j;
479 rinfo[rinfo[j + 1].index].rev_index = j + 1;
480 s = 1;
482 if (!s)
483 break;
486 pinfo->target = RC_PID_TARGET_PF;
487 pinfo->sampling_period = RC_PID_INTERVAL;
488 pinfo->coeff_p = RC_PID_COEFF_P;
489 pinfo->coeff_i = RC_PID_COEFF_I;
490 pinfo->coeff_d = RC_PID_COEFF_D;
491 pinfo->smoothing_shift = RC_PID_SMOOTHING_SHIFT;
492 pinfo->sharpen_factor = RC_PID_SHARPENING_FACTOR;
493 pinfo->sharpen_duration = RC_PID_SHARPENING_DURATION;
494 pinfo->norm_offset = RC_PID_NORM_OFFSET;
495 pinfo->fast_start = RC_PID_FAST_START;
496 pinfo->rinfo = rinfo;
497 pinfo->oldrate = 0;
499 #ifdef CONFIG_MAC80211_DEBUGFS
500 de = &pinfo->dentries;
501 de->dir = debugfs_create_dir("rc80211_pid",
502 local->hw.wiphy->debugfsdir);
503 de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR,
504 de->dir, &pinfo->target);
505 de->sampling_period = debugfs_create_u32("sampling_period",
506 S_IRUSR | S_IWUSR, de->dir,
507 &pinfo->sampling_period);
508 de->coeff_p = debugfs_create_u32("coeff_p", S_IRUSR | S_IWUSR,
509 de->dir, &pinfo->coeff_p);
510 de->coeff_i = debugfs_create_u32("coeff_i", S_IRUSR | S_IWUSR,
511 de->dir, &pinfo->coeff_i);
512 de->coeff_d = debugfs_create_u32("coeff_d", S_IRUSR | S_IWUSR,
513 de->dir, &pinfo->coeff_d);
514 de->smoothing_shift = debugfs_create_u32("smoothing_shift",
515 S_IRUSR | S_IWUSR, de->dir,
516 &pinfo->smoothing_shift);
517 de->sharpen_factor = debugfs_create_u32("sharpen_factor",
518 S_IRUSR | S_IWUSR, de->dir,
519 &pinfo->sharpen_factor);
520 de->sharpen_duration = debugfs_create_u32("sharpen_duration",
521 S_IRUSR | S_IWUSR, de->dir,
522 &pinfo->sharpen_duration);
523 de->norm_offset = debugfs_create_u32("norm_offset",
524 S_IRUSR | S_IWUSR, de->dir,
525 &pinfo->norm_offset);
526 de->fast_start = debugfs_create_bool("fast_start",
527 S_IRUSR | S_IWUSR, de->dir,
528 &pinfo->fast_start);
529 #endif
531 return pinfo;
534 static void rate_control_pid_free(void *priv)
536 struct rc_pid_info *pinfo = priv;
537 #ifdef CONFIG_MAC80211_DEBUGFS
538 struct rc_pid_debugfs_entries *de = &pinfo->dentries;
540 debugfs_remove(de->fast_start);
541 debugfs_remove(de->norm_offset);
542 debugfs_remove(de->sharpen_duration);
543 debugfs_remove(de->sharpen_factor);
544 debugfs_remove(de->smoothing_shift);
545 debugfs_remove(de->coeff_d);
546 debugfs_remove(de->coeff_i);
547 debugfs_remove(de->coeff_p);
548 debugfs_remove(de->sampling_period);
549 debugfs_remove(de->target);
550 debugfs_remove(de->dir);
551 #endif
553 kfree(pinfo->rinfo);
554 kfree(pinfo);
557 static void rate_control_pid_clear(void *priv)
561 static void *rate_control_pid_alloc_sta(void *priv, gfp_t gfp)
563 struct rc_pid_sta_info *spinfo;
565 spinfo = kzalloc(sizeof(*spinfo), gfp);
566 if (spinfo == NULL)
567 return NULL;
569 spinfo->last_sample = jiffies;
571 #ifdef CONFIG_MAC80211_DEBUGFS
572 spin_lock_init(&spinfo->events.lock);
573 init_waitqueue_head(&spinfo->events.waitqueue);
574 #endif
576 return spinfo;
579 static void rate_control_pid_free_sta(void *priv, void *priv_sta)
581 struct rc_pid_sta_info *spinfo = priv_sta;
582 kfree(spinfo);
585 static struct rate_control_ops mac80211_rcpid = {
586 .name = "pid",
587 .tx_status = rate_control_pid_tx_status,
588 .get_rate = rate_control_pid_get_rate,
589 .rate_init = rate_control_pid_rate_init,
590 .clear = rate_control_pid_clear,
591 .alloc = rate_control_pid_alloc,
592 .free = rate_control_pid_free,
593 .alloc_sta = rate_control_pid_alloc_sta,
594 .free_sta = rate_control_pid_free_sta,
595 #ifdef CONFIG_MAC80211_DEBUGFS
596 .add_sta_debugfs = rate_control_pid_add_sta_debugfs,
597 .remove_sta_debugfs = rate_control_pid_remove_sta_debugfs,
598 #endif
601 MODULE_DESCRIPTION("PID controller based rate control algorithm");
602 MODULE_AUTHOR("Stefano Brivio");
603 MODULE_AUTHOR("Mattias Nissler");
604 MODULE_LICENSE("GPL");
606 int __init rc80211_pid_init(void)
608 return ieee80211_rate_control_register(&mac80211_rcpid);
611 void rc80211_pid_exit(void)
613 ieee80211_rate_control_unregister(&mac80211_rcpid);
616 #ifdef CONFIG_MAC80211_RC_PID_MODULE
617 module_init(rc80211_pid_init);
618 module_exit(rc80211_pid_exit);
619 #endif