2010-05-13 18:48:03 +04:00
/*
2013-02-13 13:51:08 +04:00
* Copyright ( C ) 2010 - 2013 Felix Fietkau < nbd @ openwrt . org >
2010-05-13 18:48:03 +04: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>
# include <linux/debugfs.h>
# include <linux/random.h>
# include <linux/ieee80211.h>
# include <net/mac80211.h>
# include "rate.h"
# include "rc80211_minstrel.h"
# include "rc80211_minstrel_ht.h"
# define AVG_PKT_SIZE 1200
/* Number of bits for an average sized packet */
# define MCS_NBITS (AVG_PKT_SIZE << 3)
/* Number of symbols for a packet with (bps) bits per symbol */
# define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps))
2013-03-03 00:20:12 +04:00
/* Transmission time (nanoseconds) for a packet containing (syms) symbols */
2010-05-13 18:48:03 +04:00
# define MCS_SYMBOL_TIME(sgi, syms) \
( sgi ? \
2013-03-03 00:20:12 +04:00
( ( syms ) * 18000 + 4000 ) / 5 : /* syms * 3.6 us */ \
( ( syms ) * 1000 ) < < 2 /* syms * 4 us */ \
2010-05-13 18:48:03 +04:00
)
/* Transmit duration for the raw data part of an average sized packet */
# define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
2011-11-14 18:28:20 +04:00
/*
* Define group sort order : HT40 - > SGI - > # streams
*/
# define GROUP_IDX(_streams, _sgi, _ht40) \
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
MINSTREL_MAX_STREAMS * _sgi + \
_streams - 1
2010-05-13 18:48:03 +04:00
/* MCS rate information for an MCS group */
2011-11-14 18:28:20 +04:00
# define MCS_GROUP(_streams, _sgi, _ht40) \
[ GROUP_IDX ( _streams , _sgi , _ht40 ) ] = { \
2010-05-13 18:48:03 +04:00
. streams = _streams , \
. flags = \
( _sgi ? IEEE80211_TX_RC_SHORT_GI : 0 ) | \
( _ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0 ) , \
. duration = { \
MCS_DURATION ( _streams , _sgi , _ht40 ? 54 : 26 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 108 : 52 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 162 : 78 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 216 : 104 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 324 : 156 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 432 : 208 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 486 : 234 ) , \
MCS_DURATION ( _streams , _sgi , _ht40 ? 540 : 260 ) \
} \
}
2013-02-13 13:51:08 +04:00
# define CCK_DURATION(_bitrate, _short, _len) \
2013-03-03 00:20:12 +04:00
( 1000 * ( 10 /* SIFS */ + \
2013-02-13 13:51:08 +04:00
( _short ? 72 + 24 : 144 + 48 ) + \
2013-03-03 00:20:12 +04:00
( 8 * ( _len + 4 ) * 10 ) / ( _bitrate ) ) )
2013-02-13 13:51:08 +04:00
# define CCK_ACK_DURATION(_bitrate, _short) \
( CCK_DURATION ( ( _bitrate > 10 ? 20 : 10 ) , false , 60 ) + \
CCK_DURATION ( _bitrate , _short , AVG_PKT_SIZE ) )
# define CCK_DURATION_LIST(_short) \
CCK_ACK_DURATION ( 10 , _short ) , \
CCK_ACK_DURATION ( 20 , _short ) , \
CCK_ACK_DURATION ( 55 , _short ) , \
CCK_ACK_DURATION ( 110 , _short )
# define CCK_GROUP \
[ MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS ] = { \
. streams = 0 , \
. duration = { \
CCK_DURATION_LIST ( false ) , \
CCK_DURATION_LIST ( true ) \
} \
}
2010-05-13 18:48:03 +04:00
/*
* To enable sufficiently targeted rate sampling , MCS rates are divided into
* groups , based on the number of streams and flags ( HT40 , SGI ) that they
* use .
2011-11-14 18:28:20 +04:00
*
* Sortorder has to be fixed for GROUP_IDX macro to be applicable :
* HT40 - > SGI - > # streams
2010-05-13 18:48:03 +04:00
*/
const struct mcs_group minstrel_mcs_groups [ ] = {
MCS_GROUP ( 1 , 0 , 0 ) ,
MCS_GROUP ( 2 , 0 , 0 ) ,
# if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP ( 3 , 0 , 0 ) ,
# endif
MCS_GROUP ( 1 , 1 , 0 ) ,
MCS_GROUP ( 2 , 1 , 0 ) ,
# if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP ( 3 , 1 , 0 ) ,
# endif
MCS_GROUP ( 1 , 0 , 1 ) ,
MCS_GROUP ( 2 , 0 , 1 ) ,
# if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP ( 3 , 0 , 1 ) ,
# endif
MCS_GROUP ( 1 , 1 , 1 ) ,
MCS_GROUP ( 2 , 1 , 1 ) ,
# if MINSTREL_MAX_STREAMS >= 3
MCS_GROUP ( 3 , 1 , 1 ) ,
# endif
2013-02-13 13:51:08 +04:00
/* must be last */
CCK_GROUP
2010-05-13 18:48:03 +04:00
} ;
2013-02-13 13:51:08 +04:00
# define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
2010-05-13 18:48:03 +04:00
static u8 sample_table [ SAMPLE_COLUMNS ] [ MCS_GROUP_RATES ] ;
2013-04-22 18:14:42 +04:00
static void
minstrel_ht_update_rates ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi ) ;
2010-05-13 18:48:03 +04:00
/*
* Look up an MCS group index based on mac80211 rate information
*/
static int
minstrel_ht_get_group_idx ( struct ieee80211_tx_rate * rate )
{
2011-11-14 18:28:20 +04:00
return GROUP_IDX ( ( rate - > idx / MCS_GROUP_RATES ) + 1 ,
! ! ( rate - > flags & IEEE80211_TX_RC_SHORT_GI ) ,
! ! ( rate - > flags & IEEE80211_TX_RC_40_MHZ_WIDTH ) ) ;
2010-05-13 18:48:03 +04:00
}
2013-02-13 13:51:08 +04:00
static struct minstrel_rate_stats *
minstrel_ht_get_stats ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi ,
struct ieee80211_tx_rate * rate )
{
int group , idx ;
if ( rate - > flags & IEEE80211_TX_RC_MCS ) {
group = minstrel_ht_get_group_idx ( rate ) ;
idx = rate - > idx % MCS_GROUP_RATES ;
} else {
group = MINSTREL_CCK_GROUP ;
for ( idx = 0 ; idx < ARRAY_SIZE ( mp - > cck_rates ) ; idx + + )
if ( rate - > idx = = mp - > cck_rates [ idx ] )
break ;
/* short preamble */
if ( ! ( mi - > groups [ group ] . supported & BIT ( idx ) ) )
idx + = 4 ;
}
return & mi - > groups [ group ] . rates [ idx ] ;
}
2010-05-13 18:48:03 +04:00
static inline struct minstrel_rate_stats *
minstrel_get_ratestats ( struct minstrel_ht_sta * mi , int index )
{
return & mi - > groups [ index / MCS_GROUP_RATES ] . rates [ index % MCS_GROUP_RATES ] ;
}
/*
* Recalculate success probabilities and counters for a rate using EWMA
*/
static void
2011-11-15 19:44:48 +04:00
minstrel_calc_rate_ewma ( struct minstrel_rate_stats * mr )
2010-05-13 18:48:03 +04:00
{
if ( unlikely ( mr - > attempts > 0 ) ) {
mr - > sample_skipped = 0 ;
mr - > cur_prob = MINSTREL_FRAC ( mr - > success , mr - > attempts ) ;
if ( ! mr - > att_hist )
mr - > probability = mr - > cur_prob ;
else
mr - > probability = minstrel_ewma ( mr - > probability ,
mr - > cur_prob , EWMA_LEVEL ) ;
mr - > att_hist + = mr - > attempts ;
mr - > succ_hist + = mr - > success ;
} else {
mr - > sample_skipped + + ;
}
mr - > last_success = mr - > success ;
mr - > last_attempts = mr - > attempts ;
mr - > success = 0 ;
mr - > attempts = 0 ;
}
/*
* Calculate throughput based on the average A - MPDU length , taking into account
* the expected number of retransmissions and their expected length
*/
static void
2011-11-15 19:44:48 +04:00
minstrel_ht_calc_tp ( struct minstrel_ht_sta * mi , int group , int rate )
2010-05-13 18:48:03 +04:00
{
struct minstrel_rate_stats * mr ;
2013-03-03 00:20:12 +04:00
unsigned int nsecs = 0 ;
unsigned int tp ;
2013-03-16 20:00:25 +04:00
unsigned int prob ;
2010-05-13 18:48:03 +04:00
mr = & mi - > groups [ group ] . rates [ rate ] ;
2013-03-16 20:00:25 +04:00
prob = mr - > probability ;
2010-05-13 18:48:03 +04:00
2013-03-16 20:00:25 +04:00
if ( prob < MINSTREL_FRAC ( 1 , 10 ) ) {
2010-05-13 18:48:03 +04:00
mr - > cur_tp = 0 ;
return ;
}
2013-03-16 20:00:25 +04:00
/*
* For the throughput calculation , limit the probability value to 90 % to
* account for collision related packet error rate fluctuation
*/
if ( prob > MINSTREL_FRAC ( 9 , 10 ) )
prob = MINSTREL_FRAC ( 9 , 10 ) ;
2013-02-13 13:51:08 +04:00
if ( group ! = MINSTREL_CCK_GROUP )
2013-03-03 00:20:12 +04:00
nsecs = 1000 * mi - > overhead / MINSTREL_TRUNC ( mi - > avg_ampdu_len ) ;
2013-02-13 13:51:08 +04:00
2013-03-03 00:20:12 +04:00
nsecs + = minstrel_mcs_groups [ group ] . duration [ rate ] ;
tp = 1000000 * ( ( mr - > probability * 1000 ) / nsecs ) ;
mr - > cur_tp = MINSTREL_TRUNC ( tp ) ;
2010-05-13 18:48:03 +04:00
}
/*
* Update rate statistics and select new primary rates
*
* Rules for rate selection :
* - max_prob_rate must use only one stream , as a tradeoff between delivery
* probability and throughput during strong fluctuations
* - as long as the max prob rate has a probability of more than 3 / 4 , pick
* higher throughput rates , even if the probablity is a bit lower
*/
static void
minstrel_ht_update_stats ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi )
{
struct minstrel_mcs_group_data * mg ;
struct minstrel_rate_stats * mr ;
int cur_prob , cur_prob_tp , cur_tp , cur_tp2 ;
int group , i , index ;
2013-04-18 16:26:20 +04:00
bool mi_rates_valid = false ;
2010-05-13 18:48:03 +04:00
if ( mi - > ampdu_packets > 0 ) {
mi - > avg_ampdu_len = minstrel_ewma ( mi - > avg_ampdu_len ,
MINSTREL_FRAC ( mi - > ampdu_len , mi - > ampdu_packets ) , EWMA_LEVEL ) ;
mi - > ampdu_len = 0 ;
mi - > ampdu_packets = 0 ;
}
mi - > sample_slow = 0 ;
mi - > sample_count = 0 ;
for ( group = 0 ; group < ARRAY_SIZE ( minstrel_mcs_groups ) ; group + + ) {
2013-04-18 16:26:20 +04:00
bool mg_rates_valid = false ;
2010-05-13 18:48:03 +04:00
cur_prob = 0 ;
cur_prob_tp = 0 ;
cur_tp = 0 ;
cur_tp2 = 0 ;
mg = & mi - > groups [ group ] ;
if ( ! mg - > supported )
continue ;
mi - > sample_count + + ;
for ( i = 0 ; i < MCS_GROUP_RATES ; i + + ) {
if ( ! ( mg - > supported & BIT ( i ) ) )
continue ;
2013-04-18 16:26:20 +04:00
/* initialize rates selections starting indexes */
if ( ! mg_rates_valid ) {
mg - > max_tp_rate = mg - > max_tp_rate2 =
mg - > max_prob_rate = i ;
if ( ! mi_rates_valid ) {
mi - > max_tp_rate = mi - > max_tp_rate2 =
mi - > max_prob_rate = i ;
mi_rates_valid = true ;
}
mg_rates_valid = true ;
}
2010-05-13 18:48:03 +04:00
mr = & mg - > rates [ i ] ;
mr - > retry_updated = false ;
index = MCS_GROUP_RATES * group + i ;
2011-11-15 19:44:48 +04:00
minstrel_calc_rate_ewma ( mr ) ;
minstrel_ht_calc_tp ( mi , group , i ) ;
2010-05-13 18:48:03 +04:00
if ( ! mr - > cur_tp )
continue ;
if ( ( mr - > cur_tp > cur_prob_tp & & mr - > probability >
MINSTREL_FRAC ( 3 , 4 ) ) | | mr - > probability > cur_prob ) {
mg - > max_prob_rate = index ;
cur_prob = mr - > probability ;
2010-07-01 19:18:42 +04:00
cur_prob_tp = mr - > cur_tp ;
2010-05-13 18:48:03 +04:00
}
if ( mr - > cur_tp > cur_tp ) {
swap ( index , mg - > max_tp_rate ) ;
cur_tp = mr - > cur_tp ;
mr = minstrel_get_ratestats ( mi , index ) ;
}
if ( index > = mg - > max_tp_rate )
continue ;
if ( mr - > cur_tp > cur_tp2 ) {
mg - > max_tp_rate2 = index ;
cur_tp2 = mr - > cur_tp ;
}
}
}
2013-03-03 00:20:14 +04:00
/* try to sample all available rates during each interval */
mi - > sample_count * = 8 ;
2010-05-13 18:48:03 +04:00
cur_prob = 0 ;
cur_prob_tp = 0 ;
cur_tp = 0 ;
cur_tp2 = 0 ;
for ( group = 0 ; group < ARRAY_SIZE ( minstrel_mcs_groups ) ; group + + ) {
mg = & mi - > groups [ group ] ;
if ( ! mg - > supported )
continue ;
mr = minstrel_get_ratestats ( mi , mg - > max_tp_rate ) ;
if ( cur_tp < mr - > cur_tp ) {
2011-09-24 20:48:26 +04:00
mi - > max_tp_rate2 = mi - > max_tp_rate ;
cur_tp2 = cur_tp ;
2010-05-13 18:48:03 +04:00
mi - > max_tp_rate = mg - > max_tp_rate ;
cur_tp = mr - > cur_tp ;
2013-03-03 15:49:51 +04:00
mi - > max_prob_streams = minstrel_mcs_groups [ group ] . streams - 1 ;
2010-05-13 18:48:03 +04:00
}
mr = minstrel_get_ratestats ( mi , mg - > max_tp_rate2 ) ;
if ( cur_tp2 < mr - > cur_tp ) {
mi - > max_tp_rate2 = mg - > max_tp_rate2 ;
cur_tp2 = mr - > cur_tp ;
}
}
2013-03-03 15:49:51 +04:00
if ( mi - > max_prob_streams < 1 )
mi - > max_prob_streams = 1 ;
2013-03-03 00:20:13 +04:00
for ( group = 0 ; group < ARRAY_SIZE ( minstrel_mcs_groups ) ; group + + ) {
mg = & mi - > groups [ group ] ;
if ( ! mg - > supported )
continue ;
mr = minstrel_get_ratestats ( mi , mg - > max_prob_rate ) ;
if ( cur_prob_tp < mr - > cur_tp & &
2013-03-03 15:49:51 +04:00
minstrel_mcs_groups [ group ] . streams < = mi - > max_prob_streams ) {
2013-03-03 00:20:13 +04:00
mi - > max_prob_rate = mg - > max_prob_rate ;
cur_prob = mr - > cur_prob ;
cur_prob_tp = mr - > cur_tp ;
}
}
2010-05-13 18:48:03 +04:00
mi - > stats_update = jiffies ;
}
static bool
2013-02-13 13:51:08 +04:00
minstrel_ht_txstat_valid ( struct minstrel_priv * mp , struct ieee80211_tx_rate * rate )
2010-05-13 18:48:03 +04:00
{
2011-11-14 18:28:19 +04:00
if ( rate - > idx < 0 )
2010-05-13 18:48:03 +04:00
return false ;
2011-11-14 18:28:19 +04:00
if ( ! rate - > count )
2010-05-13 18:48:03 +04:00
return false ;
2013-02-13 13:51:08 +04:00
if ( rate - > flags & IEEE80211_TX_RC_MCS )
return true ;
return rate - > idx = = mp - > cck_rates [ 0 ] | |
rate - > idx = = mp - > cck_rates [ 1 ] | |
rate - > idx = = mp - > cck_rates [ 2 ] | |
rate - > idx = = mp - > cck_rates [ 3 ] ;
2010-05-13 18:48:03 +04:00
}
static void
minstrel_next_sample_idx ( struct minstrel_ht_sta * mi )
{
struct minstrel_mcs_group_data * mg ;
for ( ; ; ) {
mi - > sample_group + + ;
mi - > sample_group % = ARRAY_SIZE ( minstrel_mcs_groups ) ;
mg = & mi - > groups [ mi - > sample_group ] ;
if ( ! mg - > supported )
continue ;
if ( + + mg - > index > = MCS_GROUP_RATES ) {
mg - > index = 0 ;
if ( + + mg - > column > = ARRAY_SIZE ( sample_table ) )
mg - > column = 0 ;
}
break ;
}
}
static void
2010-06-24 19:18:38 +04:00
minstrel_downgrade_rate ( struct minstrel_ht_sta * mi , unsigned int * idx ,
bool primary )
2010-05-13 18:48:03 +04:00
{
int group , orig_group ;
orig_group = group = * idx / MCS_GROUP_RATES ;
while ( group > 0 ) {
group - - ;
if ( ! mi - > groups [ group ] . supported )
continue ;
if ( minstrel_mcs_groups [ group ] . streams >
minstrel_mcs_groups [ orig_group ] . streams )
continue ;
if ( primary )
* idx = mi - > groups [ group ] . max_tp_rate ;
else
* idx = mi - > groups [ group ] . max_tp_rate2 ;
break ;
}
}
static void
2011-11-15 19:44:48 +04:00
minstrel_aggr_check ( struct ieee80211_sta * pubsta , struct sk_buff * skb )
2010-05-13 18:48:03 +04:00
{
struct ieee80211_hdr * hdr = ( struct ieee80211_hdr * ) skb - > data ;
struct sta_info * sta = container_of ( pubsta , struct sta_info , sta ) ;
u16 tid ;
if ( unlikely ( ! ieee80211_is_data_qos ( hdr - > frame_control ) ) )
return ;
2013-08-22 10:36:41 +04:00
if ( unlikely ( skb - > protocol = = cpu_to_be16 ( ETH_P_PAE ) ) )
2010-05-13 18:48:03 +04:00
return ;
tid = * ieee80211_get_qos_ctl ( hdr ) & IEEE80211_QOS_CTL_TID_MASK ;
2010-06-10 12:21:39 +04:00
if ( likely ( sta - > ampdu_mlme . tid_tx [ tid ] ) )
2010-05-13 18:48:03 +04:00
return ;
2010-11-24 02:05:02 +03:00
if ( skb_get_queue_mapping ( skb ) = = IEEE80211_AC_VO )
return ;
2010-12-15 05:17:10 +03:00
ieee80211_start_tx_ba_session ( pubsta , tid , 5000 ) ;
2010-05-13 18:48:03 +04:00
}
static void
minstrel_ht_tx_status ( void * priv , struct ieee80211_supported_band * sband ,
struct ieee80211_sta * sta , void * priv_sta ,
struct sk_buff * skb )
{
struct minstrel_ht_sta_priv * msp = priv_sta ;
struct minstrel_ht_sta * mi = & msp - > ht ;
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( skb ) ;
struct ieee80211_tx_rate * ar = info - > status . rates ;
struct minstrel_rate_stats * rate , * rate2 ;
struct minstrel_priv * mp = priv ;
2013-04-22 18:14:42 +04:00
bool last , update = false ;
2012-11-14 00:36:27 +04:00
int i ;
2010-05-13 18:48:03 +04:00
if ( ! msp - > is_ht )
return mac80211_minstrel . tx_status ( priv , sband , sta , & msp - > legacy , skb ) ;
/* This packet was aggregated but doesn't carry status info */
if ( ( info - > flags & IEEE80211_TX_CTL_AMPDU ) & &
! ( info - > flags & IEEE80211_TX_STAT_AMPDU ) )
return ;
2010-10-11 00:14:25 +04:00
if ( ! ( info - > flags & IEEE80211_TX_STAT_AMPDU ) ) {
info - > status . ampdu_ack_len =
( info - > flags & IEEE80211_TX_STAT_ACK ? 1 : 0 ) ;
2010-05-13 18:48:03 +04:00
info - > status . ampdu_len = 1 ;
}
mi - > ampdu_packets + + ;
mi - > ampdu_len + = info - > status . ampdu_len ;
if ( ! mi - > sample_wait & & ! mi - > sample_tries & & mi - > sample_count > 0 ) {
2010-10-21 04:47:25 +04:00
mi - > sample_wait = 16 + 2 * MINSTREL_TRUNC ( mi - > avg_ampdu_len ) ;
2013-03-05 17:20:19 +04:00
mi - > sample_tries = 1 ;
2010-05-13 18:48:03 +04:00
mi - > sample_count - - ;
}
2011-03-09 14:10:18 +03:00
if ( info - > flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE )
2010-05-13 18:48:03 +04:00
mi - > sample_packets + = info - > status . ampdu_len ;
2013-02-13 13:51:08 +04:00
last = ! minstrel_ht_txstat_valid ( mp , & ar [ 0 ] ) ;
2010-05-13 18:48:03 +04:00
for ( i = 0 ; ! last ; i + + ) {
last = ( i = = IEEE80211_TX_MAX_RATES - 1 ) | |
2013-02-13 13:51:08 +04:00
! minstrel_ht_txstat_valid ( mp , & ar [ i + 1 ] ) ;
2010-05-13 18:48:03 +04:00
2013-02-13 13:51:08 +04:00
rate = minstrel_ht_get_stats ( mp , mi , & ar [ i ] ) ;
2010-05-13 18:48:03 +04:00
2010-10-11 00:14:25 +04:00
if ( last )
2010-05-13 18:48:03 +04:00
rate - > success + = info - > status . ampdu_ack_len ;
rate - > attempts + = ar [ i ] . count * info - > status . ampdu_len ;
}
/*
* check for sudden death of spatial multiplexing ,
* downgrade to a lower number of streams if necessary .
*/
rate = minstrel_get_ratestats ( mi , mi - > max_tp_rate ) ;
if ( rate - > attempts > 30 & &
MINSTREL_FRAC ( rate - > success , rate - > attempts ) <
2013-04-22 18:14:42 +04:00
MINSTREL_FRAC ( 20 , 100 ) ) {
2010-05-13 18:48:03 +04:00
minstrel_downgrade_rate ( mi , & mi - > max_tp_rate , true ) ;
2013-04-22 18:14:42 +04:00
update = true ;
}
2010-05-13 18:48:03 +04:00
rate2 = minstrel_get_ratestats ( mi , mi - > max_tp_rate2 ) ;
2010-07-01 19:19:50 +04:00
if ( rate2 - > attempts > 30 & &
MINSTREL_FRAC ( rate2 - > success , rate2 - > attempts ) <
2013-04-22 18:14:42 +04:00
MINSTREL_FRAC ( 20 , 100 ) ) {
2010-05-13 18:48:03 +04:00
minstrel_downgrade_rate ( mi , & mi - > max_tp_rate2 , false ) ;
2013-04-22 18:14:42 +04:00
update = true ;
}
2010-05-13 18:48:03 +04:00
if ( time_after ( jiffies , mi - > stats_update + ( mp - > update_interval / 2 * HZ ) / 1000 ) ) {
2013-04-22 18:14:42 +04:00
update = true ;
2010-05-13 18:48:03 +04:00
minstrel_ht_update_stats ( mp , mi ) ;
2013-02-13 13:51:08 +04:00
if ( ! ( info - > flags & IEEE80211_TX_CTL_AMPDU ) & &
mi - > max_prob_rate / MCS_GROUP_RATES ! = MINSTREL_CCK_GROUP )
2011-11-15 19:44:48 +04:00
minstrel_aggr_check ( sta , skb ) ;
2010-05-13 18:48:03 +04:00
}
2013-04-22 18:14:42 +04:00
if ( update )
minstrel_ht_update_rates ( mp , mi ) ;
2010-05-13 18:48:03 +04:00
}
static void
minstrel_calc_retransmit ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi ,
int index )
{
struct minstrel_rate_stats * mr ;
const struct mcs_group * group ;
unsigned int tx_time , tx_time_rtscts , tx_time_data ;
unsigned int cw = mp - > cw_min ;
2011-05-11 06:00:45 +04:00
unsigned int ctime = 0 ;
2010-05-13 18:48:03 +04:00
unsigned int t_slot = 9 ; /* FIXME */
unsigned int ampdu_len = MINSTREL_TRUNC ( mi - > avg_ampdu_len ) ;
2013-02-13 13:51:08 +04:00
unsigned int overhead = 0 , overhead_rtscts = 0 ;
2010-05-13 18:48:03 +04:00
mr = minstrel_get_ratestats ( mi , index ) ;
if ( mr - > probability < MINSTREL_FRAC ( 1 , 10 ) ) {
mr - > retry_count = 1 ;
mr - > retry_count_rtscts = 1 ;
return ;
}
mr - > retry_count = 2 ;
mr - > retry_count_rtscts = 2 ;
mr - > retry_updated = true ;
group = & minstrel_mcs_groups [ index / MCS_GROUP_RATES ] ;
2013-03-03 00:20:12 +04:00
tx_time_data = group - > duration [ index % MCS_GROUP_RATES ] * ampdu_len / 1000 ;
2011-05-11 06:00:45 +04:00
/* Contention time for first 2 tries */
ctime = ( t_slot * cw ) > > 1 ;
cw = min ( ( cw < < 1 ) | 1 , mp - > cw_max ) ;
ctime + = ( t_slot * cw ) > > 1 ;
cw = min ( ( cw < < 1 ) | 1 , mp - > cw_max ) ;
2013-02-13 13:51:08 +04:00
if ( index / MCS_GROUP_RATES ! = MINSTREL_CCK_GROUP ) {
overhead = mi - > overhead ;
overhead_rtscts = mi - > overhead_rtscts ;
}
2011-05-11 06:00:45 +04:00
/* Total TX time for data and Contention after first 2 tries */
2013-02-13 13:51:08 +04:00
tx_time = ctime + 2 * ( overhead + tx_time_data ) ;
tx_time_rtscts = ctime + 2 * ( overhead_rtscts + tx_time_data ) ;
2011-05-11 06:00:45 +04:00
/* See how many more tries we can fit inside segment size */
2010-05-13 18:48:03 +04:00
do {
2011-05-11 06:00:45 +04:00
/* Contention time for this try */
ctime = ( t_slot * cw ) > > 1 ;
cw = min ( ( cw < < 1 ) | 1 , mp - > cw_max ) ;
/* Total TX time after this try */
2013-02-13 13:51:08 +04:00
tx_time + = ctime + overhead + tx_time_data ;
tx_time_rtscts + = ctime + overhead_rtscts + tx_time_data ;
2011-05-11 06:00:45 +04:00
2010-05-13 18:48:03 +04:00
if ( tx_time_rtscts < mp - > segment_size )
mr - > retry_count_rtscts + + ;
} while ( ( tx_time < mp - > segment_size ) & &
( + + mr - > retry_count < mp - > max_retry ) ) ;
}
static void
minstrel_ht_set_rate ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi ,
2013-04-22 18:14:42 +04:00
struct ieee80211_sta_rates * ratetbl , int offset , int index )
2010-05-13 18:48:03 +04:00
{
const struct mcs_group * group = & minstrel_mcs_groups [ index / MCS_GROUP_RATES ] ;
struct minstrel_rate_stats * mr ;
2013-04-22 18:14:42 +04:00
u8 idx ;
u16 flags ;
2010-05-13 18:48:03 +04:00
mr = minstrel_get_ratestats ( mi , index ) ;
if ( ! mr - > retry_updated )
minstrel_calc_retransmit ( mp , mi , index ) ;
2013-04-22 18:14:42 +04:00
if ( mr - > probability < MINSTREL_FRAC ( 20 , 100 ) | | ! mr - > retry_count ) {
ratetbl - > rate [ offset ] . count = 2 ;
ratetbl - > rate [ offset ] . count_rts = 2 ;
ratetbl - > rate [ offset ] . count_cts = 2 ;
} else {
ratetbl - > rate [ offset ] . count = mr - > retry_count ;
ratetbl - > rate [ offset ] . count_cts = mr - > retry_count ;
ratetbl - > rate [ offset ] . count_rts = mr - > retry_count_rtscts ;
}
2013-02-13 13:51:08 +04:00
if ( index / MCS_GROUP_RATES = = MINSTREL_CCK_GROUP ) {
2013-04-22 18:14:42 +04:00
idx = mp - > cck_rates [ index % ARRAY_SIZE ( mp - > cck_rates ) ] ;
flags = 0 ;
} else {
idx = index % MCS_GROUP_RATES +
( group - > streams - 1 ) * MCS_GROUP_RATES ;
flags = IEEE80211_TX_RC_MCS | group - > flags ;
}
if ( offset > 0 ) {
ratetbl - > rate [ offset ] . count = ratetbl - > rate [ offset ] . count_rts ;
flags | = IEEE80211_TX_RC_USE_RTS_CTS ;
}
ratetbl - > rate [ offset ] . idx = idx ;
ratetbl - > rate [ offset ] . flags = flags ;
}
static void
minstrel_ht_update_rates ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi )
{
struct ieee80211_sta_rates * rates ;
int i = 0 ;
rates = kzalloc ( sizeof ( * rates ) , GFP_ATOMIC ) ;
if ( ! rates )
2013-02-13 13:51:08 +04:00
return ;
2013-04-22 18:14:42 +04:00
/* Start with max_tp_rate */
minstrel_ht_set_rate ( mp , mi , rates , i + + , mi - > max_tp_rate ) ;
if ( mp - > hw - > max_rates > = 3 ) {
/* At least 3 tx rates supported, use max_tp_rate2 next */
minstrel_ht_set_rate ( mp , mi , rates , i + + , mi - > max_tp_rate2 ) ;
}
if ( mp - > hw - > max_rates > = 2 ) {
/*
* At least 2 tx rates supported , use max_prob_rate next */
minstrel_ht_set_rate ( mp , mi , rates , i + + , mi - > max_prob_rate ) ;
2013-02-13 13:51:08 +04:00
}
2013-04-22 18:14:42 +04:00
rates - > rate [ i ] . idx = - 1 ;
rate_control_set_rates ( mp - > hw , mi - > sta , rates ) ;
2010-05-13 18:48:03 +04:00
}
static inline int
minstrel_get_duration ( int index )
{
const struct mcs_group * group = & minstrel_mcs_groups [ index / MCS_GROUP_RATES ] ;
return group - > duration [ index % MCS_GROUP_RATES ] ;
}
static int
minstrel_get_sample_rate ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi )
{
struct minstrel_rate_stats * mr ;
struct minstrel_mcs_group_data * mg ;
2013-03-03 15:49:51 +04:00
unsigned int sample_dur , sample_group ;
2010-05-13 18:48:03 +04:00
int sample_idx = 0 ;
if ( mi - > sample_wait > 0 ) {
mi - > sample_wait - - ;
return - 1 ;
}
if ( ! mi - > sample_tries )
return - 1 ;
mg = & mi - > groups [ mi - > sample_group ] ;
sample_idx = sample_table [ mg - > column ] [ mg - > index ] ;
mr = & mg - > rates [ sample_idx ] ;
2013-03-03 15:49:51 +04:00
sample_group = mi - > sample_group ;
sample_idx + = sample_group * MCS_GROUP_RATES ;
2011-03-09 14:10:18 +03:00
minstrel_next_sample_idx ( mi ) ;
2010-05-13 18:48:03 +04:00
2012-03-14 16:31:11 +04:00
/*
* Sampling might add some overhead ( RTS , no aggregation )
* to the frame . Hence , don ' t use sampling for the currently
2013-03-16 20:00:27 +04:00
* used rates .
2012-03-14 16:31:11 +04:00
*/
2013-03-16 20:00:27 +04:00
if ( sample_idx = = mi - > max_tp_rate | |
sample_idx = = mi - > max_tp_rate2 | |
sample_idx = = mi - > max_prob_rate )
2012-03-14 16:31:11 +04:00
return - 1 ;
2013-03-16 20:00:27 +04:00
2010-05-13 18:48:03 +04:00
/*
2013-03-16 20:00:26 +04:00
* Do not sample if the probability is already higher than 95 %
* to avoid wasting airtime .
2010-05-13 18:48:03 +04:00
*/
2013-03-16 20:00:26 +04:00
if ( mr - > probability > MINSTREL_FRAC ( 95 , 100 ) )
2011-03-09 14:10:18 +03:00
return - 1 ;
2010-05-13 18:48:03 +04:00
/*
* Make sure that lower rates get sampled only occasionally ,
* if the link is working perfectly .
*/
2013-03-03 15:49:51 +04:00
sample_dur = minstrel_get_duration ( sample_idx ) ;
if ( sample_dur > = minstrel_get_duration ( mi - > max_tp_rate2 ) & &
( mi - > max_prob_streams <
minstrel_mcs_groups [ sample_group ] . streams | |
sample_dur > = minstrel_get_duration ( mi - > max_prob_rate ) ) ) {
2010-10-21 04:47:25 +04:00
if ( mr - > sample_skipped < 20 )
2011-03-09 14:10:18 +03:00
return - 1 ;
2010-05-13 18:48:03 +04:00
if ( mi - > sample_slow + + > 2 )
2011-03-09 14:10:18 +03:00
return - 1 ;
2010-05-13 18:48:03 +04:00
}
2013-03-03 15:49:52 +04:00
mi - > sample_tries - - ;
2010-05-13 18:48:03 +04:00
return sample_idx ;
}
2013-02-13 13:51:08 +04:00
static void
minstrel_ht_check_cck_shortpreamble ( struct minstrel_priv * mp ,
struct minstrel_ht_sta * mi , bool val )
{
u8 supported = mi - > groups [ MINSTREL_CCK_GROUP ] . supported ;
if ( ! supported | | ! mi - > cck_supported_short )
return ;
if ( supported & ( mi - > cck_supported_short < < ( val * 4 ) ) )
return ;
supported ^ = mi - > cck_supported_short | ( mi - > cck_supported_short < < 4 ) ;
mi - > groups [ MINSTREL_CCK_GROUP ] . supported = supported ;
}
2010-05-13 18:48:03 +04:00
static void
minstrel_ht_get_rate ( void * priv , struct ieee80211_sta * sta , void * priv_sta ,
struct ieee80211_tx_rate_control * txrc )
{
2013-04-22 18:14:42 +04:00
const struct mcs_group * sample_group ;
2010-05-13 18:48:03 +04:00
struct ieee80211_tx_info * info = IEEE80211_SKB_CB ( txrc - > skb ) ;
2013-04-22 18:14:42 +04:00
struct ieee80211_tx_rate * rate = & info - > status . rates [ 0 ] ;
2010-05-13 18:48:03 +04:00
struct minstrel_ht_sta_priv * msp = priv_sta ;
struct minstrel_ht_sta * mi = & msp - > ht ;
struct minstrel_priv * mp = priv ;
int sample_idx ;
if ( rate_control_send_low ( sta , priv_sta , txrc ) )
return ;
if ( ! msp - > is_ht )
return mac80211_minstrel . get_rate ( priv , sta , & msp - > legacy , txrc ) ;
info - > flags | = mi - > tx_flags ;
2013-02-13 13:51:08 +04:00
minstrel_ht_check_cck_shortpreamble ( mp , mi , txrc - > short_preamble ) ;
2011-08-01 13:32:53 +04:00
/* Don't use EAPOL frames for sampling on non-mrr hw */
if ( mp - > hw - > max_rates = = 1 & &
2013-07-02 20:09:12 +04:00
( info - > control . flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO ) )
2011-08-01 13:32:53 +04:00
sample_idx = - 1 ;
else
sample_idx = minstrel_get_sample_rate ( mp , mi ) ;
2011-05-20 22:29:17 +04:00
# ifdef CONFIG_MAC80211_DEBUGFS
/* use fixed index if set */
2012-07-09 21:25:09 +04:00
if ( mp - > fixed_rate_idx ! = - 1 ) {
mi - > max_tp_rate = mp - > fixed_rate_idx ;
mi - > max_tp_rate2 = mp - > fixed_rate_idx ;
mi - > max_prob_rate = mp - > fixed_rate_idx ;
sample_idx = - 1 ;
}
2011-05-20 22:29:17 +04:00
# endif
2010-05-13 18:48:03 +04:00
mi - > total_packets + + ;
/* wraparound */
if ( mi - > total_packets = = ~ 0 ) {
mi - > total_packets = 0 ;
mi - > sample_packets = 0 ;
}
2013-04-22 18:14:42 +04:00
if ( sample_idx < 0 )
return ;
sample_group = & minstrel_mcs_groups [ sample_idx / MCS_GROUP_RATES ] ;
info - > flags | = IEEE80211_TX_CTL_RATE_CTRL_PROBE ;
rate - > idx = sample_idx % MCS_GROUP_RATES +
( sample_group - > streams - 1 ) * MCS_GROUP_RATES ;
rate - > flags = IEEE80211_TX_RC_MCS | sample_group - > flags ;
rate - > count = 1 ;
2010-05-13 18:48:03 +04:00
}
2013-02-13 13:51:08 +04:00
static void
minstrel_ht_update_cck ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi ,
struct ieee80211_supported_band * sband ,
struct ieee80211_sta * sta )
{
int i ;
if ( sband - > band ! = IEEE80211_BAND_2GHZ )
return ;
mi - > cck_supported = 0 ;
mi - > cck_supported_short = 0 ;
for ( i = 0 ; i < 4 ; i + + ) {
if ( ! rate_supported ( sta , sband - > band , mp - > cck_rates [ i ] ) )
continue ;
mi - > cck_supported | = BIT ( i ) ;
if ( sband - > bitrates [ i ] . flags & IEEE80211_RATE_SHORT_PREAMBLE )
mi - > cck_supported_short | = BIT ( i ) ;
}
mi - > groups [ MINSTREL_CCK_GROUP ] . supported = mi - > cck_supported ;
}
2010-05-13 18:48:03 +04:00
static void
minstrel_ht_update_caps ( void * priv , struct ieee80211_supported_band * sband ,
2013-07-08 18:55:50 +04:00
struct cfg80211_chan_def * chandef ,
2012-03-28 12:58:37 +04:00
struct ieee80211_sta * sta , void * priv_sta )
2010-05-13 18:48:03 +04:00
{
struct minstrel_priv * mp = priv ;
struct minstrel_ht_sta_priv * msp = priv_sta ;
struct minstrel_ht_sta * mi = & msp - > ht ;
struct ieee80211_mcs_info * mcs = & sta - > ht_cap . mcs ;
u16 sta_cap = sta - > ht_cap . cap ;
2011-03-25 17:30:38 +03:00
int n_supported = 0 ;
2010-05-13 18:48:03 +04:00
int ack_dur ;
int stbc ;
int i ;
/* fall back to the old minstrel for legacy stations */
2011-03-25 17:30:38 +03:00
if ( ! sta - > ht_cap . ht_supported )
goto use_legacy ;
2010-05-13 18:48:03 +04:00
BUILD_BUG_ON ( ARRAY_SIZE ( minstrel_mcs_groups ) ! =
2013-02-13 13:51:08 +04:00
MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1 ) ;
2010-05-13 18:48:03 +04:00
msp - > is_ht = true ;
memset ( mi , 0 , sizeof ( * mi ) ) ;
2013-04-22 18:14:42 +04:00
mi - > sta = sta ;
2010-05-13 18:48:03 +04:00
mi - > stats_update = jiffies ;
2013-07-08 18:55:51 +04:00
ack_dur = ieee80211_frame_duration ( sband - > band , 10 , 60 , 1 , 1 , 0 ) ;
mi - > overhead = ieee80211_frame_duration ( sband - > band , 0 , 60 , 1 , 1 , 0 ) ;
mi - > overhead + = ack_dur ;
2010-05-13 18:48:03 +04:00
mi - > overhead_rtscts = mi - > overhead + 2 * ack_dur ;
mi - > avg_ampdu_len = MINSTREL_FRAC ( 1 , 1 ) ;
/* When using MRR, sample more on the first attempt, without delay */
if ( mp - > has_mrr ) {
mi - > sample_count = 16 ;
mi - > sample_wait = 0 ;
} else {
mi - > sample_count = 8 ;
mi - > sample_wait = 8 ;
}
mi - > sample_tries = 4 ;
stbc = ( sta_cap & IEEE80211_HT_CAP_RX_STBC ) > >
IEEE80211_HT_CAP_RX_STBC_SHIFT ;
mi - > tx_flags | = stbc < < IEEE80211_TX_CTL_STBC_SHIFT ;
if ( sta_cap & IEEE80211_HT_CAP_LDPC_CODING )
mi - > tx_flags | = IEEE80211_TX_CTL_LDPC ;
for ( i = 0 ; i < ARRAY_SIZE ( mi - > groups ) ; i + + ) {
mi - > groups [ i ] . supported = 0 ;
2013-02-13 13:51:08 +04:00
if ( i = = MINSTREL_CCK_GROUP ) {
minstrel_ht_update_cck ( mp , mi , sband , sta ) ;
continue ;
}
2010-05-13 18:48:03 +04:00
if ( minstrel_mcs_groups [ i ] . flags & IEEE80211_TX_RC_SHORT_GI ) {
2013-02-07 14:47:44 +04:00
if ( minstrel_mcs_groups [ i ] . flags & IEEE80211_TX_RC_40_MHZ_WIDTH ) {
if ( ! ( sta_cap & IEEE80211_HT_CAP_SGI_40 ) )
continue ;
} else {
if ( ! ( sta_cap & IEEE80211_HT_CAP_SGI_20 ) )
continue ;
}
2010-05-13 18:48:03 +04:00
}
2013-02-07 14:47:44 +04:00
if ( minstrel_mcs_groups [ i ] . flags & IEEE80211_TX_RC_40_MHZ_WIDTH & &
sta - > bandwidth < IEEE80211_STA_RX_BW_40 )
2010-05-13 18:48:03 +04:00
continue ;
2012-03-09 17:13:45 +04:00
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
2013-02-12 17:21:00 +04:00
if ( sta - > smps_mode = = IEEE80211_SMPS_STATIC & &
2012-03-09 17:13:45 +04:00
minstrel_mcs_groups [ i ] . streams > 1 )
continue ;
2010-05-13 18:48:03 +04:00
mi - > groups [ i ] . supported =
mcs - > rx_mask [ minstrel_mcs_groups [ i ] . streams - 1 ] ;
2011-03-25 17:30:38 +03:00
if ( mi - > groups [ i ] . supported )
n_supported + + ;
2010-05-13 18:48:03 +04:00
}
2011-03-25 17:30:38 +03:00
if ( ! n_supported )
goto use_legacy ;
2013-04-22 18:14:42 +04:00
/* create an initial rate table with the lowest supported rates */
2013-04-18 16:26:21 +04:00
minstrel_ht_update_stats ( mp , mi ) ;
2013-04-22 18:14:42 +04:00
minstrel_ht_update_rates ( mp , mi ) ;
2013-04-18 16:26:21 +04:00
2011-03-25 17:30:38 +03:00
return ;
use_legacy :
msp - > is_ht = false ;
memset ( & msp - > legacy , 0 , sizeof ( msp - > legacy ) ) ;
msp - > legacy . r = msp - > ratelist ;
msp - > legacy . sample_table = msp - > sample_table ;
2013-07-08 18:55:50 +04:00
return mac80211_minstrel . rate_init ( priv , sband , chandef , sta ,
& msp - > legacy ) ;
2010-05-13 18:48:03 +04:00
}
static void
minstrel_ht_rate_init ( void * priv , struct ieee80211_supported_band * sband ,
2013-07-08 18:55:50 +04:00
struct cfg80211_chan_def * chandef ,
2010-05-13 18:48:03 +04:00
struct ieee80211_sta * sta , void * priv_sta )
{
2013-07-08 18:55:50 +04:00
minstrel_ht_update_caps ( priv , sband , chandef , sta , priv_sta ) ;
2010-05-13 18:48:03 +04:00
}
static void
minstrel_ht_rate_update ( void * priv , struct ieee80211_supported_band * sband ,
2013-07-08 18:55:50 +04:00
struct cfg80211_chan_def * chandef ,
2010-05-13 18:48:03 +04:00
struct ieee80211_sta * sta , void * priv_sta ,
2012-03-28 12:58:37 +04:00
u32 changed )
2010-05-13 18:48:03 +04:00
{
2013-07-08 18:55:50 +04:00
minstrel_ht_update_caps ( priv , sband , chandef , sta , priv_sta ) ;
2010-05-13 18:48:03 +04:00
}
static void *
minstrel_ht_alloc_sta ( void * priv , struct ieee80211_sta * sta , gfp_t gfp )
{
struct ieee80211_supported_band * sband ;
struct minstrel_ht_sta_priv * msp ;
struct minstrel_priv * mp = priv ;
struct ieee80211_hw * hw = mp - > hw ;
int max_rates = 0 ;
int i ;
for ( i = 0 ; i < IEEE80211_NUM_BANDS ; i + + ) {
sband = hw - > wiphy - > bands [ i ] ;
if ( sband & & sband - > n_bitrates > max_rates )
max_rates = sband - > n_bitrates ;
}
2012-06-29 17:26:27 +04:00
msp = kzalloc ( sizeof ( * msp ) , gfp ) ;
2010-05-13 18:48:03 +04:00
if ( ! msp )
return NULL ;
msp - > ratelist = kzalloc ( sizeof ( struct minstrel_rate ) * max_rates , gfp ) ;
if ( ! msp - > ratelist )
goto error ;
msp - > sample_table = kmalloc ( SAMPLE_COLUMNS * max_rates , gfp ) ;
if ( ! msp - > sample_table )
goto error1 ;
return msp ;
error1 :
2010-07-22 15:14:19 +04:00
kfree ( msp - > ratelist ) ;
2010-05-13 18:48:03 +04:00
error :
kfree ( msp ) ;
return NULL ;
}
static void
minstrel_ht_free_sta ( void * priv , struct ieee80211_sta * sta , void * priv_sta )
{
struct minstrel_ht_sta_priv * msp = priv_sta ;
kfree ( msp - > sample_table ) ;
kfree ( msp - > ratelist ) ;
kfree ( msp ) ;
}
static void *
minstrel_ht_alloc ( struct ieee80211_hw * hw , struct dentry * debugfsdir )
{
return mac80211_minstrel . alloc ( hw , debugfsdir ) ;
}
static void
minstrel_ht_free ( void * priv )
{
mac80211_minstrel . free ( priv ) ;
}
static struct rate_control_ops mac80211_minstrel_ht = {
. name = " minstrel_ht " ,
. tx_status = minstrel_ht_tx_status ,
. get_rate = minstrel_ht_get_rate ,
. rate_init = minstrel_ht_rate_init ,
. rate_update = minstrel_ht_rate_update ,
. alloc_sta = minstrel_ht_alloc_sta ,
. free_sta = minstrel_ht_free_sta ,
. alloc = minstrel_ht_alloc ,
. free = minstrel_ht_free ,
# ifdef CONFIG_MAC80211_DEBUGFS
. add_sta_debugfs = minstrel_ht_add_sta_debugfs ,
. remove_sta_debugfs = minstrel_ht_remove_sta_debugfs ,
# endif
} ;
static void
init_sample_table ( void )
{
int col , i , new_idx ;
u8 rnd [ MCS_GROUP_RATES ] ;
memset ( sample_table , 0xff , sizeof ( sample_table ) ) ;
for ( col = 0 ; col < SAMPLE_COLUMNS ; col + + ) {
for ( i = 0 ; i < MCS_GROUP_RATES ; i + + ) {
get_random_bytes ( rnd , sizeof ( rnd ) ) ;
new_idx = ( i + rnd [ i ] ) % MCS_GROUP_RATES ;
while ( sample_table [ col ] [ new_idx ] ! = 0xff )
new_idx = ( new_idx + 1 ) % MCS_GROUP_RATES ;
sample_table [ col ] [ new_idx ] = i ;
}
}
}
int __init
rc80211_minstrel_ht_init ( void )
{
init_sample_table ( ) ;
return ieee80211_rate_control_register ( & mac80211_minstrel_ht ) ;
}
void
rc80211_minstrel_ht_exit ( void )
{
ieee80211_rate_control_unregister ( & mac80211_minstrel_ht ) ;
}