2007-12-19 01:25:57 +01:00
/*
* Copyright 2002 - 2005 , Instant802 Networks , Inc .
* Copyright 2005 , Devicescape Software , Inc .
* Copyright 2007 , Mattias Nissler < mattias . nissler @ gmx . de >
2007-12-19 01:26:34 +01:00
* Copyright 2007 , Stefano Brivio < stefano . brivio @ polimi . it >
2007-12-19 01:25:57 +01:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/netdevice.h>
# include <linux/types.h>
# include <linux/skbuff.h>
2008-01-02 15:17:03 +01:00
# include <linux/debugfs.h>
2007-12-19 01:25:57 +01:00
# include <net/mac80211.h>
# include "ieee80211_rate.h"
2007-12-19 01:27:18 +01:00
# include "rc80211_pid.h"
2007-12-19 01:25:57 +01:00
/* This is an implementation of a TX rate control algorithm that uses a PID
* controller . Given a target failed frames rate , the controller decides about
* TX rate changes to meet the target failed frames rate .
*
* The controller basically computes the following :
*
2007-12-19 01:26:52 +01:00
* adj = CP * err + CI * err_avg + CD * ( err - last_err ) * ( 1 + sharpening )
2007-12-19 01:25:57 +01:00
*
* where
* adj adjustment value that is used to switch TX rate ( see below )
* err current error : target vs . current failed frames percentage
* last_err last error
* err_avg average ( i . e . poor man ' s integral ) of recent errors
2007-12-19 01:26:52 +01:00
* sharpening non - zero when fast response is needed ( i . e . right after
* association or no frames sent for a long time ) , heading
* to zero over time
2007-12-19 01:25:57 +01:00
* CP Proportional coefficient
* CI Integral coefficient
* CD Derivative coefficient
*
* CP , CI , CD are subject to careful tuning .
*
* The integral component uses a exponential moving average approach instead of
* an actual sliding window . The advantage is that we don ' t need to keep an
* array of the last N error values and computation is easier .
*
2007-12-19 01:26:34 +01:00
* Once we have the adj value , we map it to a rate by means of a learning
* algorithm . This algorithm keeps the state of the percentual failed frames
* difference between rates . The behaviour of the lowest available rate is kept
* as a reference value , and every time we switch between two rates , we compute
* the difference between the failed frames each rate exhibited . By doing so ,
* we compare behaviours which different rates exhibited in adjacent timeslices ,
* thus the comparison is minimally affected by external conditions . This
* difference gets propagated to the whole set of measurements , so that the
* reference is always the same . Periodically , we normalize this set so that
* recent events weigh the most . By comparing the adj value with this set , we
* avoid pejorative switches to lower rates and allow for switches to higher
* rates if they behaved well .
2007-12-19 01:25:57 +01:00
*
* Note that for the computations we use a fixed - point representation to avoid
* floating point arithmetic . Hence , all values are shifted left by
* RC_PID_ARITH_SHIFT .
*/
2007-12-19 01:26:34 +01:00
/* Shift the adjustment so that we won't switch to a lower rate if it exhibited
* a worse failed frames behaviour and we ' ll choose the highest rate whose
* failed frames behaviour is not worse than the one of the original rate
* target . While at it , check that the adjustment is within the ranges . Then ,
* provide the new rate index . */
static int rate_control_pid_shift_adjust ( struct rc_pid_rateinfo * r ,
int adj , int cur , int l )
{
int i , j , k , tmp ;
2007-12-23 04:41:19 +01:00
j = r [ cur ] . rev_index ;
i = j + adj ;
2007-12-19 01:26:34 +01:00
2007-12-23 04:41:19 +01:00
if ( i < 0 )
return r [ 0 ] . index ;
if ( i > = l - 1 )
return r [ l - 1 ] . index ;
2007-12-19 01:26:34 +01:00
2007-12-23 04:41:19 +01:00
tmp = i ;
2007-12-19 01:26:34 +01:00
if ( adj < 0 ) {
2007-12-23 04:41:19 +01:00
for ( k = j ; k > = i ; k - - )
if ( r [ k ] . diff < = r [ j ] . diff )
tmp = k ;
} else {
for ( k = i + 1 ; k + i < l ; k + + )
if ( r [ k ] . diff < = r [ i ] . diff )
tmp = k ;
2007-12-19 01:26:34 +01:00
}
2007-12-23 04:41:19 +01:00
return r [ tmp ] . index ;
2007-12-19 01:26:34 +01:00
}
2007-12-19 01:25:57 +01:00
static void rate_control_pid_adjust_rate ( struct ieee80211_local * local ,
2007-12-19 01:26:34 +01:00
struct sta_info * sta , int adj ,
struct rc_pid_rateinfo * rinfo )
2007-12-19 01:25:57 +01:00
{
struct ieee80211_sub_if_data * sdata ;
struct ieee80211_hw_mode * mode ;
2007-12-19 01:26:34 +01:00
int newidx ;
2007-12-19 01:25:57 +01:00
int maxrate ;
int back = ( adj > 0 ) ? 1 : - 1 ;
sdata = IEEE80211_DEV_TO_SUB_IF ( sta - > dev ) ;
mode = local - > oper_hw_mode ;
maxrate = sdata - > bss ? sdata - > bss - > max_ratectrl_rateidx : - 1 ;
2007-12-19 01:26:34 +01:00
newidx = rate_control_pid_shift_adjust ( rinfo , adj , sta - > txrate ,
mode - > num_rates ) ;
2007-12-19 01:25:57 +01:00
while ( newidx ! = sta - > txrate ) {
if ( rate_supported ( sta , mode , newidx ) & &
( maxrate < 0 | | newidx < = maxrate ) ) {
sta - > txrate = newidx ;
break ;
}
newidx + = back ;
}
2007-12-19 01:27:18 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_rate_change (
& ( ( struct rc_pid_sta_info * ) sta - > rate_ctrl_priv ) - > events ,
newidx , mode - > rates [ newidx ] . rate ) ;
# endif
2007-12-19 01:25:57 +01:00
}
2007-12-19 01:26:34 +01:00
/* Normalize the failed frames per-rate differences. */
2007-12-20 13:27:26 +01:00
static void rate_control_pid_normalize ( struct rc_pid_info * pinfo , int l )
2007-12-19 01:26:34 +01:00
{
2007-12-20 13:27:26 +01:00
int i , norm_offset = pinfo - > norm_offset ;
struct rc_pid_rateinfo * r = pinfo - > rinfo ;
2007-12-19 01:26:34 +01:00
2007-12-20 13:27:26 +01:00
if ( r [ 0 ] . diff > norm_offset )
r [ 0 ] . diff - = norm_offset ;
else if ( r [ 0 ] . diff < - norm_offset )
r [ 0 ] . diff + = norm_offset ;
2007-12-19 01:26:34 +01:00
for ( i = 0 ; i < l - 1 ; i + + )
2007-12-20 13:27:26 +01:00
if ( r [ i + 1 ] . diff > r [ i ] . diff + norm_offset )
r [ i + 1 ] . diff - = norm_offset ;
2007-12-19 01:26:34 +01:00
else if ( r [ i + 1 ] . diff < = r [ i ] . diff )
2007-12-20 13:27:26 +01:00
r [ i + 1 ] . diff + = norm_offset ;
2007-12-19 01:26:34 +01:00
}
2007-12-19 01:25:57 +01:00
static void rate_control_pid_sample ( struct rc_pid_info * pinfo ,
struct ieee80211_local * local ,
struct sta_info * sta )
{
struct rc_pid_sta_info * spinfo = sta - > rate_ctrl_priv ;
2007-12-19 01:26:34 +01:00
struct rc_pid_rateinfo * rinfo = pinfo - > rinfo ;
struct ieee80211_hw_mode * mode ;
2007-12-19 01:25:57 +01:00
u32 pf ;
s32 err_avg ;
2007-12-20 13:27:26 +01:00
u32 err_prop ;
u32 err_int ;
u32 err_der ;
2007-12-19 01:26:34 +01:00
int adj , i , j , tmp ;
2007-12-20 13:27:26 +01:00
unsigned long period ;
2007-12-19 01:25:57 +01:00
2007-12-19 01:26:34 +01:00
mode = local - > oper_hw_mode ;
2007-12-19 01:25:57 +01:00
spinfo = sta - > rate_ctrl_priv ;
2007-12-19 01:26:52 +01:00
/* In case nothing happened during the previous control interval, turn
* the sharpening factor on . */
2007-12-20 13:27:26 +01:00
period = ( HZ * pinfo - > sampling_period + 500 ) / 1000 ;
if ( ! period )
period = 1 ;
if ( jiffies - spinfo - > last_sample > 2 * period )
spinfo - > sharp_cnt = pinfo - > sharpen_duration ;
2007-12-19 01:26:52 +01:00
2007-12-19 01:25:57 +01:00
spinfo - > last_sample = jiffies ;
2007-12-19 01:26:52 +01:00
/* This should never happen, but in case, we assume the old sample is
2007-12-19 01:25:57 +01:00
* still a good measurement and copy it . */
2007-12-19 01:26:52 +01:00
if ( unlikely ( spinfo - > tx_num_xmit = = 0 ) )
2007-12-19 01:25:57 +01:00
pf = spinfo - > last_pf ;
else {
pf = spinfo - > tx_num_failed * 100 / spinfo - > tx_num_xmit ;
pf < < = RC_PID_ARITH_SHIFT ;
}
2007-12-19 01:26:52 +01:00
spinfo - > tx_num_xmit = 0 ;
spinfo - > tx_num_failed = 0 ;
2007-12-19 01:26:34 +01:00
/* If we just switched rate, update the rate behaviour info. */
if ( pinfo - > oldrate ! = sta - > txrate ) {
i = rinfo [ pinfo - > oldrate ] . rev_index ;
j = rinfo [ sta - > txrate ] . rev_index ;
tmp = ( pf - spinfo - > last_pf ) ;
tmp = RC_PID_DO_ARITH_RIGHT_SHIFT ( tmp , RC_PID_ARITH_SHIFT ) ;
rinfo [ j ] . diff = rinfo [ i ] . diff + tmp ;
pinfo - > oldrate = sta - > txrate ;
}
2007-12-20 13:27:26 +01:00
rate_control_pid_normalize ( pinfo , mode - > num_rates ) ;
2007-12-19 01:26:34 +01:00
2007-12-19 01:25:57 +01:00
/* Compute the proportional, integral and derivative errors. */
2007-12-23 04:39:17 +01:00
err_prop = ( pinfo - > target < < RC_PID_ARITH_SHIFT ) - pf ;
2007-12-19 01:25:57 +01:00
2007-12-20 13:27:26 +01:00
err_avg = spinfo - > err_avg_sc > > pinfo - > smoothing_shift ;
2007-12-19 01:25:57 +01:00
spinfo - > err_avg_sc = spinfo - > err_avg_sc - err_avg + err_prop ;
2007-12-20 13:27:26 +01:00
err_int = spinfo - > err_avg_sc > > pinfo - > smoothing_shift ;
2007-12-19 01:25:57 +01:00
2007-12-20 13:27:26 +01:00
err_der = ( pf - spinfo - > last_pf ) *
( 1 + pinfo - > sharpen_factor * spinfo - > sharp_cnt ) ;
2007-12-19 01:25:57 +01:00
spinfo - > last_pf = pf ;
2007-12-19 01:26:52 +01:00
if ( spinfo - > sharp_cnt )
spinfo - > sharp_cnt - - ;
2007-12-19 01:25:57 +01:00
2007-12-19 01:27:18 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_pf_sample ( & spinfo - > events , pf , err_prop , err_int ,
err_der ) ;
# endif
2007-12-19 01:25:57 +01:00
/* Compute the controller output. */
adj = ( err_prop * pinfo - > coeff_p + err_int * pinfo - > coeff_i
+ err_der * pinfo - > coeff_d ) ;
2007-12-19 01:26:34 +01:00
adj = RC_PID_DO_ARITH_RIGHT_SHIFT ( adj , 2 * RC_PID_ARITH_SHIFT ) ;
2007-12-19 01:25:57 +01:00
/* Change rate. */
if ( adj )
2007-12-19 01:26:34 +01:00
rate_control_pid_adjust_rate ( local , sta , adj , rinfo ) ;
2007-12-19 01:25:57 +01:00
}
static void rate_control_pid_tx_status ( void * priv , struct net_device * dev ,
struct sk_buff * skb ,
struct ieee80211_tx_status * status )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2008-01-03 21:05:37 -08:00
struct ieee80211_sub_if_data * sdata ;
2007-12-19 01:25:57 +01:00
struct rc_pid_info * pinfo = priv ;
struct sta_info * sta ;
struct rc_pid_sta_info * spinfo ;
2007-12-20 13:27:26 +01:00
unsigned long period ;
2007-12-19 01:25:57 +01:00
sta = sta_info_get ( local , hdr - > addr1 ) ;
if ( ! sta )
return ;
2008-01-03 21:05:37 -08:00
/* Don't update the state if we're not controlling the rate. */
sdata = IEEE80211_DEV_TO_SUB_IF ( sta - > dev ) ;
if ( sdata - > bss & & sdata - > bss - > force_unicast_rateidx > - 1 ) {
sta - > txrate = sdata - > bss - > max_ratectrl_rateidx ;
return ;
}
2007-12-19 01:25:57 +01:00
/* Ignore all frames that were sent with a different rate than the rate
* we currently advise mac80211 to use . */
if ( status - > control . rate ! = & local - > oper_hw_mode - > rates [ sta - > txrate ] )
2007-12-23 04:43:57 +01:00
goto ignore ;
2007-12-19 01:25:57 +01:00
spinfo = sta - > rate_ctrl_priv ;
spinfo - > tx_num_xmit + + ;
2007-12-19 01:27:18 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_status ( & spinfo - > events , status ) ;
# endif
2007-12-19 01:25:57 +01:00
/* We count frames that totally failed to be transmitted as two bad
* frames , those that made it out but had some retries as one good and
* one bad frame . */
if ( status - > excessive_retries ) {
spinfo - > tx_num_failed + = 2 ;
spinfo - > tx_num_xmit + + ;
} else if ( status - > retry_count ) {
spinfo - > tx_num_failed + + ;
spinfo - > tx_num_xmit + + ;
}
if ( status - > excessive_retries ) {
sta - > tx_retry_failed + + ;
sta - > tx_num_consecutive_failures + + ;
sta - > tx_num_mpdu_fail + + ;
} else {
sta - > last_ack_rssi [ 0 ] = sta - > last_ack_rssi [ 1 ] ;
sta - > last_ack_rssi [ 1 ] = sta - > last_ack_rssi [ 2 ] ;
sta - > last_ack_rssi [ 2 ] = status - > ack_signal ;
sta - > tx_num_consecutive_failures = 0 ;
sta - > tx_num_mpdu_ok + + ;
}
sta - > tx_retry_count + = status - > retry_count ;
sta - > tx_num_mpdu_fail + = status - > retry_count ;
/* Update PID controller state. */
2007-12-20 13:27:26 +01:00
period = ( HZ * pinfo - > sampling_period + 500 ) / 1000 ;
if ( ! period )
period = 1 ;
if ( time_after ( jiffies , spinfo - > last_sample + period ) )
2007-12-19 01:25:57 +01:00
rate_control_pid_sample ( pinfo , local , sta ) ;
2007-12-23 04:43:57 +01:00
ignore :
2007-12-19 01:25:57 +01:00
sta_info_put ( sta ) ;
}
static void rate_control_pid_get_rate ( void * priv , struct net_device * dev ,
struct ieee80211_hw_mode * mode ,
struct sk_buff * skb ,
struct rate_selection * sel )
{
struct ieee80211_local * local = wdev_priv ( dev - > ieee80211_ptr ) ;
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
2007-12-25 19:33:16 -05:00
struct ieee80211_sub_if_data * sdata ;
2007-12-19 01:25:57 +01:00
struct sta_info * sta ;
int rateidx ;
2007-12-25 19:33:16 -05:00
u16 fc ;
2007-12-19 01:25:57 +01:00
sta = sta_info_get ( local , hdr - > addr1 ) ;
2007-12-25 19:33:16 -05:00
/* Send management frames and broadcast/multicast data using lowest
* rate . */
fc = le16_to_cpu ( hdr - > frame_control ) ;
if ( ( fc & IEEE80211_FCTL_FTYPE ) ! = IEEE80211_FTYPE_DATA | |
is_multicast_ether_addr ( hdr - > addr1 ) | | ! sta ) {
sel - > rate = rate_lowest ( local , mode , sta ) ;
if ( sta )
sta_info_put ( sta ) ;
2007-12-19 01:25:57 +01:00
return ;
}
2007-12-25 19:33:16 -05:00
/* If a forced rate is in effect, select it. */
sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
if ( sdata - > bss & & sdata - > bss - > force_unicast_rateidx > - 1 )
sta - > txrate = sdata - > bss - > force_unicast_rateidx ;
2007-12-19 01:25:57 +01:00
rateidx = sta - > txrate ;
if ( rateidx > = mode - > num_rates )
rateidx = mode - > num_rates - 1 ;
2007-12-25 19:33:16 -05:00
sta - > last_txrate = rateidx ;
2007-12-19 01:25:57 +01:00
sta_info_put ( sta ) ;
sel - > rate = & mode - > rates [ rateidx ] ;
2007-12-19 01:27:18 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
rate_control_pid_event_tx_rate (
& ( ( struct rc_pid_sta_info * ) sta - > rate_ctrl_priv ) - > events ,
rateidx , mode - > rates [ rateidx ] . rate ) ;
# endif
2007-12-19 01:25:57 +01:00
}
static void rate_control_pid_rate_init ( void * priv , void * priv_sta ,
struct ieee80211_local * local ,
struct sta_info * sta )
{
/* TODO: This routine should consider using RSSI from previous packets
* as we need to have IEEE 802.1 X auth succeed immediately after assoc . .
* Until that method is implemented , we will use the lowest supported
* rate as a workaround . */
sta - > txrate = rate_lowest_index ( local , local - > oper_hw_mode , sta ) ;
}
static void * rate_control_pid_alloc ( struct ieee80211_local * local )
{
struct rc_pid_info * pinfo ;
2007-12-19 01:26:34 +01:00
struct rc_pid_rateinfo * rinfo ;
struct ieee80211_hw_mode * mode ;
int i , j , tmp ;
bool s ;
2007-12-20 13:27:26 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
struct rc_pid_debugfs_entries * de ;
# endif
2007-12-19 01:25:57 +01:00
pinfo = kmalloc ( sizeof ( * pinfo ) , GFP_ATOMIC ) ;
2007-12-19 01:26:34 +01:00
if ( ! pinfo )
return NULL ;
/* We can safely assume that oper_hw_mode won't change unless we get
* reinitialized . */
mode = local - > oper_hw_mode ;
rinfo = kmalloc ( sizeof ( * rinfo ) * mode - > num_rates , GFP_ATOMIC ) ;
if ( ! rinfo ) {
kfree ( pinfo ) ;
return NULL ;
}
/* Sort the rates. This is optimized for the most common case (i.e.
* almost - sorted CCK + OFDM rates ) . Kind of bubble - sort with reversed
* mapping too . */
for ( i = 0 ; i < mode - > num_rates ; i + + ) {
rinfo [ i ] . index = i ;
rinfo [ i ] . rev_index = i ;
2007-12-20 13:27:26 +01:00
if ( pinfo - > fast_start )
2007-12-19 01:26:34 +01:00
rinfo [ i ] . diff = 0 ;
else
2007-12-20 13:27:26 +01:00
rinfo [ i ] . diff = i * pinfo - > norm_offset ;
2007-12-19 01:26:34 +01:00
}
for ( i = 1 ; i < mode - > num_rates ; i + + ) {
s = 0 ;
for ( j = 0 ; j < mode - > num_rates - i ; j + + )
if ( unlikely ( mode - > rates [ rinfo [ j ] . index ] . rate >
mode - > rates [ rinfo [ j + 1 ] . index ] . rate ) ) {
tmp = rinfo [ j ] . index ;
rinfo [ j ] . index = rinfo [ j + 1 ] . index ;
rinfo [ j + 1 ] . index = tmp ;
rinfo [ rinfo [ j ] . index ] . rev_index = j ;
rinfo [ rinfo [ j + 1 ] . index ] . rev_index = j + 1 ;
s = 1 ;
}
if ( ! s )
break ;
}
2007-12-19 01:25:57 +01:00
pinfo - > target = RC_PID_TARGET_PF ;
2007-12-20 13:27:26 +01:00
pinfo - > sampling_period = RC_PID_INTERVAL ;
2007-12-19 01:25:57 +01:00
pinfo - > coeff_p = RC_PID_COEFF_P ;
pinfo - > coeff_i = RC_PID_COEFF_I ;
pinfo - > coeff_d = RC_PID_COEFF_D ;
2007-12-20 13:27:26 +01:00
pinfo - > smoothing_shift = RC_PID_SMOOTHING_SHIFT ;
pinfo - > sharpen_factor = RC_PID_SHARPENING_FACTOR ;
pinfo - > sharpen_duration = RC_PID_SHARPENING_DURATION ;
pinfo - > norm_offset = RC_PID_NORM_OFFSET ;
pinfo - > fast_start = RC_PID_FAST_START ;
2007-12-19 01:26:34 +01:00
pinfo - > rinfo = rinfo ;
pinfo - > oldrate = 0 ;
2007-12-19 01:25:57 +01:00
2007-12-20 13:27:26 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
de = & pinfo - > dentries ;
de - > dir = debugfs_create_dir ( " rc80211_pid " ,
local - > hw . wiphy - > debugfsdir ) ;
de - > target = debugfs_create_u32 ( " target_pf " , S_IRUSR | S_IWUSR ,
de - > dir , & pinfo - > target ) ;
de - > sampling_period = debugfs_create_u32 ( " sampling_period " ,
S_IRUSR | S_IWUSR , de - > dir ,
& pinfo - > sampling_period ) ;
de - > coeff_p = debugfs_create_u32 ( " coeff_p " , S_IRUSR | S_IWUSR ,
de - > dir , & pinfo - > coeff_p ) ;
de - > coeff_i = debugfs_create_u32 ( " coeff_i " , S_IRUSR | S_IWUSR ,
de - > dir , & pinfo - > coeff_i ) ;
de - > coeff_d = debugfs_create_u32 ( " coeff_d " , S_IRUSR | S_IWUSR ,
de - > dir , & pinfo - > coeff_d ) ;
de - > smoothing_shift = debugfs_create_u32 ( " smoothing_shift " ,
S_IRUSR | S_IWUSR , de - > dir ,
& pinfo - > smoothing_shift ) ;
de - > sharpen_factor = debugfs_create_u32 ( " sharpen_factor " ,
S_IRUSR | S_IWUSR , de - > dir ,
& pinfo - > sharpen_factor ) ;
de - > sharpen_duration = debugfs_create_u32 ( " sharpen_duration " ,
S_IRUSR | S_IWUSR , de - > dir ,
& pinfo - > sharpen_duration ) ;
de - > norm_offset = debugfs_create_u32 ( " norm_offset " ,
S_IRUSR | S_IWUSR , de - > dir ,
& pinfo - > norm_offset ) ;
de - > fast_start = debugfs_create_bool ( " fast_start " ,
S_IRUSR | S_IWUSR , de - > dir ,
& pinfo - > fast_start ) ;
# endif
2007-12-19 01:25:57 +01:00
return pinfo ;
}
static void rate_control_pid_free ( void * priv )
{
struct rc_pid_info * pinfo = priv ;
2007-12-20 13:27:26 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
struct rc_pid_debugfs_entries * de = & pinfo - > dentries ;
debugfs_remove ( de - > fast_start ) ;
debugfs_remove ( de - > norm_offset ) ;
debugfs_remove ( de - > sharpen_duration ) ;
debugfs_remove ( de - > sharpen_factor ) ;
debugfs_remove ( de - > smoothing_shift ) ;
debugfs_remove ( de - > coeff_d ) ;
debugfs_remove ( de - > coeff_i ) ;
debugfs_remove ( de - > coeff_p ) ;
debugfs_remove ( de - > sampling_period ) ;
debugfs_remove ( de - > target ) ;
debugfs_remove ( de - > dir ) ;
# endif
2007-12-19 01:26:34 +01:00
kfree ( pinfo - > rinfo ) ;
2007-12-19 01:25:57 +01:00
kfree ( pinfo ) ;
}
static void rate_control_pid_clear ( void * priv )
{
}
static void * rate_control_pid_alloc_sta ( void * priv , gfp_t gfp )
{
struct rc_pid_sta_info * spinfo ;
spinfo = kzalloc ( sizeof ( * spinfo ) , gfp ) ;
2007-12-19 01:27:18 +01:00
if ( spinfo = = NULL )
return NULL ;
2008-01-17 00:38:29 +01:00
spinfo - > last_sample = jiffies ;
2007-12-19 01:27:18 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
spin_lock_init ( & spinfo - > events . lock ) ;
init_waitqueue_head ( & spinfo - > events . waitqueue ) ;
# endif
2007-12-19 01:25:57 +01:00
return spinfo ;
}
static void rate_control_pid_free_sta ( void * priv , void * priv_sta )
{
struct rc_pid_sta_info * spinfo = priv_sta ;
kfree ( spinfo ) ;
}
2008-01-02 15:17:03 +01:00
static struct rate_control_ops mac80211_rcpid = {
2007-12-19 01:25:57 +01:00
. name = " pid " ,
. tx_status = rate_control_pid_tx_status ,
. get_rate = rate_control_pid_get_rate ,
. rate_init = rate_control_pid_rate_init ,
. clear = rate_control_pid_clear ,
. alloc = rate_control_pid_alloc ,
. free = rate_control_pid_free ,
. alloc_sta = rate_control_pid_alloc_sta ,
. free_sta = rate_control_pid_free_sta ,
2007-12-19 01:27:18 +01:00
# ifdef CONFIG_MAC80211_DEBUGFS
. add_sta_debugfs = rate_control_pid_add_sta_debugfs ,
. remove_sta_debugfs = rate_control_pid_remove_sta_debugfs ,
# endif
2007-12-19 01:25:57 +01:00
} ;
2008-01-02 15:17:03 +01:00
MODULE_DESCRIPTION ( " PID controller based rate control algorithm " ) ;
MODULE_AUTHOR ( " Stefano Brivio " ) ;
MODULE_AUTHOR ( " Mattias Nissler " ) ;
MODULE_LICENSE ( " GPL " ) ;
int __init rc80211_pid_init ( void )
{
return ieee80211_rate_control_register ( & mac80211_rcpid ) ;
}
2008-01-31 19:31:57 +01:00
void rc80211_pid_exit ( void )
2008-01-02 15:17:03 +01:00
{
ieee80211_rate_control_unregister ( & mac80211_rcpid ) ;
}
# ifdef CONFIG_MAC80211_RC_PID_MODULE
module_init ( rc80211_pid_init ) ;
module_exit ( rc80211_pid_exit ) ;
# endif