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>
2014-10-21 12:38:38 +04:00
# include <linux/moduleparam.h>
2010-05-13 18:48:03 +04:00
# include <linux/ieee80211.h>
# include <net/mac80211.h>
# include "rate.h"
# include "rc80211_minstrel.h"
# include "rc80211_minstrel_ht.h"
2015-03-13 12:54:44 +03:00
# define AVG_AMPDU_SIZE 16
2010-05-13 18:48:03 +04:00
# define AVG_PKT_SIZE 1200
/* Number of bits for an average sized packet */
2015-03-13 12:54:44 +03:00
# define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
2010-05-13 18:48:03 +04:00
/* Number of symbols for a packet with (bps) bits per symbol */
2014-05-19 13:24:19 +04:00
# define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps))
2010-05-13 18:48:03 +04:00
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 */
2015-03-13 12:54:44 +03:00
# define MCS_DURATION(streams, sgi, bps) \
( MCS_SYMBOL_TIME ( sgi , MCS_NSYMS ( ( streams ) * ( bps ) ) ) / AVG_AMPDU_SIZE )
2010-05-13 18:48:03 +04:00
2014-10-20 17:46:00 +04:00
# define BW_20 0
# define BW_40 1
2014-10-21 12:38:38 +04:00
# define BW_80 2
2014-10-20 17:46:00 +04:00
2011-11-14 18:28:20 +04:00
/*
* Define group sort order : HT40 - > SGI - > # streams
*/
# define GROUP_IDX(_streams, _sgi, _ht40) \
2014-10-20 17:46:00 +04:00
MINSTREL_HT_GROUP_0 + \
2011-11-14 18:28:20 +04:00
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
2014-10-20 17:46:00 +04:00
MINSTREL_MAX_STREAMS * _sgi + \
2011-11-14 18:28:20 +04:00
_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 = \
2014-10-20 17:46:01 +04:00
IEEE80211_TX_RC_MCS | \
2010-05-13 18:48:03 +04:00
( _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 ) \
} \
}
2014-10-21 12:38:38 +04:00
# define VHT_GROUP_IDX(_streams, _sgi, _bw) \
( MINSTREL_VHT_GROUP_0 + \
MINSTREL_MAX_STREAMS * 2 * ( _bw ) + \
MINSTREL_MAX_STREAMS * ( _sgi ) + \
( _streams ) - 1 )
# define BW2VBPS(_bw, r3, r2, r1) \
( _bw = = BW_80 ? r3 : _bw = = BW_40 ? r2 : r1 )
# define VHT_GROUP(_streams, _sgi, _bw) \
[ VHT_GROUP_IDX ( _streams , _sgi , _bw ) ] = { \
. streams = _streams , \
. flags = \
IEEE80211_TX_RC_VHT_MCS | \
( _sgi ? IEEE80211_TX_RC_SHORT_GI : 0 ) | \
( _bw = = BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \
_bw = = BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0 ) , \
. duration = { \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 117 , 54 , 26 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 234 , 108 , 52 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 351 , 162 , 78 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 468 , 216 , 104 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 702 , 324 , 156 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 936 , 432 , 208 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 1053 , 486 , 234 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 1170 , 540 , 260 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 1404 , 648 , 312 ) ) , \
MCS_DURATION ( _streams , _sgi , \
BW2VBPS ( _bw , 1560 , 720 , 346 ) ) \
} \
}
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-12-18 11:44:16 +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 )
2014-10-20 17:46:00 +04:00
# define CCK_GROUP \
[ MINSTREL_CCK_GROUP ] = { \
. streams = 0 , \
2014-10-20 17:46:01 +04:00
. flags = 0 , \
2014-10-20 17:46:00 +04:00
. duration = { \
CCK_DURATION_LIST ( false ) , \
CCK_DURATION_LIST ( true ) \
} \
2013-02-13 13:51:08 +04:00
}
2014-10-21 12:38:38 +04:00
# ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
static bool minstrel_vht_only = true ;
module_param ( minstrel_vht_only , bool , 0644 ) ;
MODULE_PARM_DESC ( minstrel_vht_only ,
" Use only VHT rates when VHT is supported by sta. " ) ;
# endif
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 :
2014-10-20 17:46:00 +04:00
* BW - > SGI - > # streams
2010-05-13 18:48:03 +04:00
*/
const struct mcs_group minstrel_mcs_groups [ ] = {
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 1 , 0 , BW_20 ) ,
MCS_GROUP ( 2 , 0 , BW_20 ) ,
2010-05-13 18:48:03 +04:00
# if MINSTREL_MAX_STREAMS >= 3
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 3 , 0 , BW_20 ) ,
2010-05-13 18:48:03 +04:00
# endif
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 1 , 1 , BW_20 ) ,
MCS_GROUP ( 2 , 1 , BW_20 ) ,
2010-05-13 18:48:03 +04:00
# if MINSTREL_MAX_STREAMS >= 3
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 3 , 1 , BW_20 ) ,
2010-05-13 18:48:03 +04:00
# endif
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 1 , 0 , BW_40 ) ,
MCS_GROUP ( 2 , 0 , BW_40 ) ,
2010-05-13 18:48:03 +04:00
# if MINSTREL_MAX_STREAMS >= 3
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 3 , 0 , BW_40 ) ,
2010-05-13 18:48:03 +04:00
# endif
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 1 , 1 , BW_40 ) ,
MCS_GROUP ( 2 , 1 , BW_40 ) ,
2010-05-13 18:48:03 +04:00
# if MINSTREL_MAX_STREAMS >= 3
2014-10-20 17:46:00 +04:00
MCS_GROUP ( 3 , 1 , BW_40 ) ,
2010-05-13 18:48:03 +04:00
# endif
2013-02-13 13:51:08 +04:00
2014-10-21 12:38:38 +04:00
CCK_GROUP ,
# ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
VHT_GROUP ( 1 , 0 , BW_20 ) ,
VHT_GROUP ( 2 , 0 , BW_20 ) ,
# if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP ( 3 , 0 , BW_20 ) ,
# endif
VHT_GROUP ( 1 , 1 , BW_20 ) ,
VHT_GROUP ( 2 , 1 , BW_20 ) ,
# if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP ( 3 , 1 , BW_20 ) ,
# endif
2010-05-13 18:48:03 +04:00
2014-10-21 12:38:38 +04:00
VHT_GROUP ( 1 , 0 , BW_40 ) ,
VHT_GROUP ( 2 , 0 , BW_40 ) ,
# if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP ( 3 , 0 , BW_40 ) ,
# endif
VHT_GROUP ( 1 , 1 , BW_40 ) ,
VHT_GROUP ( 2 , 1 , BW_40 ) ,
# if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP ( 3 , 1 , BW_40 ) ,
# endif
VHT_GROUP ( 1 , 0 , BW_80 ) ,
VHT_GROUP ( 2 , 0 , BW_80 ) ,
# if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP ( 3 , 0 , BW_80 ) ,
# endif
VHT_GROUP ( 1 , 1 , BW_80 ) ,
VHT_GROUP ( 2 , 1 , BW_80 ) ,
# if MINSTREL_MAX_STREAMS >= 3
VHT_GROUP ( 3 , 1 , BW_80 ) ,
# endif
# endif
} ;
2013-02-13 13:51:08 +04:00
2014-01-21 03:32:52 +04:00
static u8 sample_table [ SAMPLE_COLUMNS ] [ MCS_GROUP_RATES ] __read_mostly ;
2010-05-13 18:48:03 +04:00
2013-04-22 18:14:42 +04:00
static void
minstrel_ht_update_rates ( struct minstrel_priv * mp , struct minstrel_ht_sta * mi ) ;
2014-10-21 12:38:38 +04:00
/*
* Some VHT MCSes are invalid ( when Ndbps / Nes is not an integer )
* e . g for MCS9 @ 20 MHzx1Nss : Ndbps = 8 x52 * ( 5 / 6 ) Nes = 1
*
* Returns the valid mcs map for struct minstrel_mcs_group_data . supported
*/
static u16
minstrel_get_valid_vht_rates ( int bw , int nss , __le16 mcs_map )
{
u16 mask = 0 ;
if ( bw = = BW_20 ) {
if ( nss ! = 3 & & nss ! = 6 )
mask = BIT ( 9 ) ;
} else if ( bw = = BW_80 ) {
if ( nss = = 3 | | nss = = 7 )
mask = BIT ( 6 ) ;
else if ( nss = = 6 )
mask = BIT ( 9 ) ;
} else {
WARN_ON ( bw ! = BW_40 ) ;
}
switch ( ( le16_to_cpu ( mcs_map ) > > ( 2 * ( nss - 1 ) ) ) & 3 ) {
case IEEE80211_VHT_MCS_SUPPORT_0_7 :
mask | = 0x300 ;
break ;
case IEEE80211_VHT_MCS_SUPPORT_0_8 :
mask | = 0x200 ;
break ;
case IEEE80211_VHT_MCS_SUPPORT_0_9 :
break ;
default :
mask = 0x3ff ;
}
return 0x3ff & ~ mask ;
}
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 )
{
2014-09-29 04:36:30 +04:00
return GROUP_IDX ( ( rate - > idx / 8 ) + 1 ,
2011-11-14 18:28:20 +04:00
! ! ( rate - > flags & IEEE80211_TX_RC_SHORT_GI ) ,
! ! ( rate - > flags & IEEE80211_TX_RC_40_MHZ_WIDTH ) ) ;
2010-05-13 18:48:03 +04:00
}
2014-10-21 12:38:38 +04:00
static int
minstrel_vht_get_group_idx ( struct ieee80211_tx_rate * rate )
{
return VHT_GROUP_IDX ( ieee80211_rate_get_vht_nss ( rate ) ,
! ! ( rate - > flags & IEEE80211_TX_RC_SHORT_GI ) ,
! ! ( rate - > flags & IEEE80211_TX_RC_40_MHZ_WIDTH ) +
2 * ! ! ( rate - > flags & IEEE80211_TX_RC_80_MHZ_WIDTH ) ) ;
}
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 ) ;
2013-11-11 16:12:55 +04:00
idx = rate - > idx % 8 ;
2014-10-21 12:38:38 +04:00
} else if ( rate - > flags & IEEE80211_TX_RC_VHT_MCS ) {
group = minstrel_vht_get_group_idx ( rate ) ;
idx = ieee80211_rate_get_vht_mcs ( rate ) ;
2013-02-13 13:51:08 +04:00
} 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 ] ;
}
/*
2015-03-24 23:09:40 +03:00
* Return current throughput based on the average A - MPDU length , taking into
* account the expected number of retransmissions and their expected length
2010-05-13 18:48:03 +04:00
*/
2015-03-24 23:09:40 +03:00
int
minstrel_ht_get_tp_avg ( struct minstrel_ht_sta * mi , int group , int rate )
2010-05-13 18:48:03 +04:00
{
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs ;
2013-03-03 00:20:12 +04:00
unsigned int nsecs = 0 ;
2015-03-24 23:09:39 +03:00
unsigned int tmp_prob_ewma ;
2010-05-13 18:48:03 +04:00
2015-03-24 23:09:39 +03:00
mrs = & mi - > groups [ group ] . rates [ rate ] ;
tmp_prob_ewma = mrs - > prob_ewma ;
2010-05-13 18:48:03 +04:00
2015-03-24 23:09:39 +03:00
/* do not account throughput if sucess prob is below 10% */
2015-03-24 23:09:40 +03:00
if ( mrs - > prob_ewma < MINSTREL_FRAC ( 10 , 100 ) )
return 0 ;
2010-05-13 18:48:03 +04:00
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
*/
2015-03-24 23:09:39 +03:00
if ( mrs - > prob_ewma > MINSTREL_FRAC ( 90 , 100 ) )
tmp_prob_ewma = MINSTREL_FRAC ( 90 , 100 ) ;
2013-03-16 20:00:25 +04:00
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 ] ;
2014-05-19 13:24:19 +04:00
/* prob is scaled - see MINSTREL_FRAC above */
2015-03-24 23:09:40 +03:00
return MINSTREL_TRUNC ( 100000 * ( ( tmp_prob_ewma * 1000 ) / nsecs ) ) ;
2010-05-13 18:48:03 +04:00
}
2014-09-10 01:22:14 +04:00
/*
* Find & sort topmost throughput rates
*
* If multiple rates provide equal throughput the sorting is based on their
* current success probability . Higher success probability is preferred among
* MCS groups , CCK rates do not provide aggregation and are therefore at last .
*/
static void
2014-10-20 17:45:59 +04:00
minstrel_ht_sort_best_tp_rates ( struct minstrel_ht_sta * mi , u16 index ,
u16 * tp_list )
2014-09-10 01:22:14 +04:00
{
2015-03-24 23:09:40 +03:00
int cur_group , cur_idx , cur_tp_avg , cur_prob ;
int tmp_group , tmp_idx , tmp_tp_avg , tmp_prob ;
2014-09-10 01:22:14 +04:00
int j = MAX_THR_RATES ;
cur_group = index / MCS_GROUP_RATES ;
cur_idx = index % MCS_GROUP_RATES ;
2015-03-24 23:09:40 +03:00
cur_tp_avg = minstrel_ht_get_tp_avg ( mi , cur_group , cur_idx ) ;
2015-03-24 23:09:39 +03:00
cur_prob = mi - > groups [ cur_group ] . rates [ cur_idx ] . prob_ewma ;
2014-09-10 01:22:14 +04:00
2014-11-19 00:35:31 +03:00
do {
2014-09-10 01:22:14 +04:00
tmp_group = tp_list [ j - 1 ] / MCS_GROUP_RATES ;
tmp_idx = tp_list [ j - 1 ] % MCS_GROUP_RATES ;
2015-03-24 23:09:40 +03:00
tmp_tp_avg = minstrel_ht_get_tp_avg ( mi , tmp_group , tmp_idx ) ;
2015-03-24 23:09:39 +03:00
tmp_prob = mi - > groups [ tmp_group ] . rates [ tmp_idx ] . prob_ewma ;
2015-03-24 23:09:40 +03:00
if ( cur_tp_avg < tmp_tp_avg | |
( cur_tp_avg = = tmp_tp_avg & & cur_prob < = tmp_prob ) )
2014-11-19 00:35:31 +03:00
break ;
j - - ;
} while ( j > 0 ) ;
2014-09-10 01:22:14 +04:00
if ( j < MAX_THR_RATES - 1 ) {
memmove ( & tp_list [ j + 1 ] , & tp_list [ j ] , ( sizeof ( * tp_list ) *
( MAX_THR_RATES - ( j + 1 ) ) ) ) ;
}
if ( j < MAX_THR_RATES )
tp_list [ j ] = index ;
}
/*
* Find and set the topmost probability rate per sta and per group
*/
static void
2014-10-20 17:45:59 +04:00
minstrel_ht_set_best_prob_rate ( struct minstrel_ht_sta * mi , u16 index )
2014-09-10 01:22:14 +04:00
{
struct minstrel_mcs_group_data * mg ;
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs ;
2015-03-24 23:09:40 +03:00
int tmp_group , tmp_idx , tmp_tp_avg , tmp_prob ;
int max_tp_group , cur_tp_avg , cur_group , cur_idx ;
int max_group_prob_rate_group , max_group_prob_rate_idx ;
int max_group_prob_rate_tp_avg ;
2014-09-10 01:22:14 +04:00
2015-03-24 23:09:40 +03:00
cur_group = index / MCS_GROUP_RATES ;
cur_idx = index % MCS_GROUP_RATES ;
2014-09-10 01:22:14 +04:00
mg = & mi - > groups [ index / MCS_GROUP_RATES ] ;
2015-03-24 23:09:39 +03:00
mrs = & mg - > rates [ index % MCS_GROUP_RATES ] ;
2014-09-10 01:22:14 +04:00
tmp_group = mi - > max_prob_rate / MCS_GROUP_RATES ;
tmp_idx = mi - > max_prob_rate % MCS_GROUP_RATES ;
2015-03-24 23:09:40 +03:00
tmp_tp_avg = minstrel_ht_get_tp_avg ( mi , tmp_group , tmp_idx ) ;
2015-03-24 23:09:39 +03:00
tmp_prob = mi - > groups [ tmp_group ] . rates [ tmp_idx ] . prob_ewma ;
2014-09-10 01:22:14 +04:00
/* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from
* MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */
max_tp_group = mi - > max_tp_rate [ 0 ] / MCS_GROUP_RATES ;
if ( ( index / MCS_GROUP_RATES = = MINSTREL_CCK_GROUP ) & &
( max_tp_group ! = MINSTREL_CCK_GROUP ) )
return ;
2015-03-24 23:09:39 +03:00
if ( mrs - > prob_ewma > MINSTREL_FRAC ( 75 , 100 ) ) {
2015-03-24 23:09:40 +03:00
cur_tp_avg = minstrel_ht_get_tp_avg ( mi , cur_group , cur_idx ) ;
if ( cur_tp_avg > tmp_tp_avg )
2014-09-10 01:22:14 +04:00
mi - > max_prob_rate = index ;
2015-03-24 23:09:40 +03:00
max_group_prob_rate_group = mg - > max_group_prob_rate /
MCS_GROUP_RATES ;
max_group_prob_rate_idx = mg - > max_group_prob_rate %
MCS_GROUP_RATES ;
max_group_prob_rate_tp_avg = minstrel_ht_get_tp_avg ( mi ,
max_group_prob_rate_group ,
max_group_prob_rate_idx ) ;
if ( cur_tp_avg > max_group_prob_rate_tp_avg )
2014-09-10 01:22:14 +04:00
mg - > max_group_prob_rate = index ;
} else {
2015-03-24 23:09:39 +03:00
if ( mrs - > prob_ewma > tmp_prob )
2014-09-10 01:22:14 +04:00
mi - > max_prob_rate = index ;
2015-03-24 23:09:39 +03:00
if ( mrs - > prob_ewma > mg - > rates [ mg - > max_group_prob_rate ] . prob_ewma )
2014-09-10 01:22:14 +04:00
mg - > max_group_prob_rate = index ;
}
}
/*
* Assign new rate set per sta and use CCK rates only if the fastest
* rate ( max_tp_rate [ 0 ] ) is from CCK group . This prohibits such sorted
* rate sets where MCS and CCK rates are mixed , because CCK rates can
* not use aggregation .
*/
static void
minstrel_ht_assign_best_tp_rates ( struct minstrel_ht_sta * mi ,
2014-10-20 17:45:59 +04:00
u16 tmp_mcs_tp_rate [ MAX_THR_RATES ] ,
u16 tmp_cck_tp_rate [ MAX_THR_RATES ] )
2014-09-10 01:22:14 +04:00
{
unsigned int tmp_group , tmp_idx , tmp_cck_tp , tmp_mcs_tp ;
int i ;
tmp_group = tmp_cck_tp_rate [ 0 ] / MCS_GROUP_RATES ;
tmp_idx = tmp_cck_tp_rate [ 0 ] % MCS_GROUP_RATES ;
2015-03-24 23:09:40 +03:00
tmp_cck_tp = minstrel_ht_get_tp_avg ( mi , tmp_group , tmp_idx ) ;
2014-09-10 01:22:14 +04:00
tmp_group = tmp_mcs_tp_rate [ 0 ] / MCS_GROUP_RATES ;
tmp_idx = tmp_mcs_tp_rate [ 0 ] % MCS_GROUP_RATES ;
2015-03-24 23:09:40 +03:00
tmp_mcs_tp = minstrel_ht_get_tp_avg ( mi , tmp_group , tmp_idx ) ;
2014-09-10 01:22:14 +04:00
if ( tmp_cck_tp > tmp_mcs_tp ) {
for ( i = 0 ; i < MAX_THR_RATES ; i + + ) {
minstrel_ht_sort_best_tp_rates ( mi , tmp_cck_tp_rate [ i ] ,
tmp_mcs_tp_rate ) ;
}
}
}
/*
* Try to increase robustness of max_prob rate by decrease number of
* streams if possible .
*/
static inline void
minstrel_ht_prob_rate_reduce_streams ( struct minstrel_ht_sta * mi )
{
struct minstrel_mcs_group_data * mg ;
2015-03-24 23:09:40 +03:00
int tmp_max_streams , group , tmp_idx ;
2014-09-10 01:22:14 +04:00
int tmp_tp = 0 ;
tmp_max_streams = minstrel_mcs_groups [ mi - > max_tp_rate [ 0 ] /
MCS_GROUP_RATES ] . streams ;
for ( group = 0 ; group < ARRAY_SIZE ( minstrel_mcs_groups ) ; group + + ) {
mg = & mi - > groups [ group ] ;
if ( ! mg - > supported | | group = = MINSTREL_CCK_GROUP )
continue ;
2015-03-24 23:09:40 +03:00
tmp_idx = mg - > max_group_prob_rate % MCS_GROUP_RATES ;
if ( tmp_tp < minstrel_ht_get_tp_avg ( mi , group , tmp_idx ) & &
2014-09-10 01:22:14 +04:00
( minstrel_mcs_groups [ group ] . streams < tmp_max_streams ) ) {
mi - > max_prob_rate = mg - > max_group_prob_rate ;
2015-03-24 23:09:40 +03:00
tmp_tp = minstrel_ht_get_tp_avg ( mi , group ,
tmp_idx ) ;
2014-09-10 01:22:14 +04:00
}
}
}
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
2014-09-10 01:22:14 +04:00
* - as long as the max prob rate has a probability of more than 75 % , pick
2010-05-13 18:48:03 +04:00
* 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 ;
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs ;
2014-09-10 01:22:14 +04:00
int group , i , j ;
2014-10-20 17:45:59 +04:00
u16 tmp_mcs_tp_rate [ MAX_THR_RATES ] , tmp_group_tp_rate [ MAX_THR_RATES ] ;
u16 tmp_cck_tp_rate [ MAX_THR_RATES ] , index ;
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 ;
2014-09-10 01:22:14 +04:00
/* Initialize global rate indexes */
for ( j = 0 ; j < MAX_THR_RATES ; j + + ) {
tmp_mcs_tp_rate [ j ] = 0 ;
tmp_cck_tp_rate [ j ] = 0 ;
}
2013-04-18 16:26:20 +04:00
2014-09-10 01:22:14 +04:00
/* Find best rate sets within all MCS groups*/
for ( group = 0 ; group < ARRAY_SIZE ( minstrel_mcs_groups ) ; group + + ) {
2010-05-13 18:48:03 +04:00
mg = & mi - > groups [ group ] ;
if ( ! mg - > supported )
continue ;
mi - > sample_count + + ;
2014-09-10 01:22:14 +04:00
/* (re)Initialize group rate indexes */
for ( j = 0 ; j < MAX_THR_RATES ; j + + )
tmp_group_tp_rate [ j ] = group ;
2010-05-13 18:48:03 +04:00
for ( i = 0 ; i < MCS_GROUP_RATES ; i + + ) {
if ( ! ( mg - > supported & BIT ( i ) ) )
continue ;
2013-11-14 02:07:07 +04:00
index = MCS_GROUP_RATES * group + i ;
2015-03-24 23:09:39 +03:00
mrs = & mg - > rates [ i ] ;
mrs - > retry_updated = false ;
minstrel_calc_rate_stats ( mrs ) ;
2010-05-13 18:48:03 +04:00
2015-03-24 23:09:40 +03:00
if ( minstrel_ht_get_tp_avg ( mi , group , i ) = = 0 )
2010-05-13 18:48:03 +04:00
continue ;
2014-09-10 01:22:14 +04:00
/* Find max throughput rate set */
if ( group ! = MINSTREL_CCK_GROUP ) {
minstrel_ht_sort_best_tp_rates ( mi , index ,
tmp_mcs_tp_rate ) ;
} else if ( group = = MINSTREL_CCK_GROUP ) {
minstrel_ht_sort_best_tp_rates ( mi , index ,
tmp_cck_tp_rate ) ;
2010-05-13 18:48:03 +04:00
}
2014-09-10 01:22:14 +04:00
/* Find max throughput rate set within a group */
minstrel_ht_sort_best_tp_rates ( mi , index ,
tmp_group_tp_rate ) ;
2010-05-13 18:48:03 +04:00
2014-09-10 01:22:14 +04:00
/* Find max probability rate per group and global */
minstrel_ht_set_best_prob_rate ( mi , index ) ;
2010-05-13 18:48:03 +04:00
}
2014-09-10 01:22:14 +04:00
memcpy ( mg - > max_group_tp_rate , tmp_group_tp_rate ,
sizeof ( mg - > max_group_tp_rate ) ) ;
2010-05-13 18:48:03 +04:00
}
2014-09-10 01:22:14 +04:00
/* Assign new rate set per sta */
minstrel_ht_assign_best_tp_rates ( mi , tmp_mcs_tp_rate , tmp_cck_tp_rate ) ;
memcpy ( mi - > max_tp_rate , tmp_mcs_tp_rate , sizeof ( mi - > max_tp_rate ) ) ;
2013-03-03 00:20:13 +04:00
2014-09-10 01:22:14 +04:00
/* Try to increase robustness of max_prob_rate*/
minstrel_ht_prob_rate_reduce_streams ( mi ) ;
/* try to sample all available rates during each interval */
mi - > sample_count * = 8 ;
2013-03-03 00:20:13 +04:00
2013-08-27 18:59:47 +04:00
# ifdef CONFIG_MAC80211_DEBUGFS
/* use fixed index if set */
if ( mp - > fixed_rate_idx ! = - 1 ) {
2014-09-10 01:22:14 +04:00
for ( i = 0 ; i < 4 ; i + + )
mi - > max_tp_rate [ i ] = mp - > fixed_rate_idx ;
2013-08-27 18:59:47 +04:00
mi - > max_prob_rate = mp - > fixed_rate_idx ;
}
# endif
2013-03-03 00:20:13 +04:00
2014-09-10 01:22:14 +04:00
/* Reset update timer */
2015-03-24 23:09:39 +03:00
mi - > last_stats_update = jiffies ;
2010-05-13 18:48:03 +04:00
}
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 ;
2014-10-21 12:38:38 +04:00
if ( rate - > flags & IEEE80211_TX_RC_MCS | |
rate - > flags & IEEE80211_TX_RC_VHT_MCS )
2013-02-13 13:51:08 +04:00
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
2015-03-24 23:09:39 +03:00
minstrel_set_next_sample_idx ( struct minstrel_ht_sta * mi )
2010-05-13 18:48:03 +04:00
{
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
2014-10-20 17:45:59 +04:00
minstrel_downgrade_rate ( struct minstrel_ht_sta * mi , u16 * 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 )
2014-09-10 01:22:14 +04:00
* idx = mi - > groups [ group ] . max_group_tp_rate [ 0 ] ;
2010-05-13 18:48:03 +04:00
else
2014-09-10 01:22:14 +04:00
* idx = mi - > groups [ group ] . max_group_tp_rate [ 1 ] ;
2010-05-13 18:48:03 +04:00
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 ;
2014-11-16 02:27:55 +03:00
if ( skb_get_queue_mapping ( skb ) = = IEEE80211_AC_VO )
return ;
2010-05-13 18:48:03 +04:00
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-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 ,
2014-11-19 22:08:10 +03:00
struct ieee80211_tx_info * info )
2010-05-13 18:48:03 +04:00
{
struct minstrel_ht_sta_priv * msp = priv_sta ;
struct minstrel_ht_sta * mi = & msp - > ht ;
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 )
2014-11-19 22:08:10 +03:00
return mac80211_minstrel . tx_status_noskb ( priv , sband , sta ,
& msp - > legacy , info ) ;
2010-05-13 18:48:03 +04:00
/* 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 .
*/
2014-09-10 01:22:14 +04:00
rate = minstrel_get_ratestats ( mi , mi - > max_tp_rate [ 0 ] ) ;
2010-05-13 18:48:03 +04:00
if ( rate - > attempts > 30 & &
MINSTREL_FRAC ( rate - > success , rate - > attempts ) <
2013-04-22 18:14:42 +04:00
MINSTREL_FRAC ( 20 , 100 ) ) {
2014-09-10 01:22:14 +04:00
minstrel_downgrade_rate ( mi , & mi - > max_tp_rate [ 0 ] , true ) ;
2013-04-22 18:14:42 +04:00
update = true ;
}
2010-05-13 18:48:03 +04:00
2014-09-10 01:22:14 +04:00
rate2 = minstrel_get_ratestats ( mi , mi - > max_tp_rate [ 1 ] ) ;
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 ) ) {
2014-09-10 01:22:14 +04:00
minstrel_downgrade_rate ( mi , & mi - > max_tp_rate [ 1 ] , false ) ;
2013-04-22 18:14:42 +04:00
update = true ;
}
2010-05-13 18:48:03 +04:00
2015-03-24 23:09:39 +03:00
if ( time_after ( jiffies , mi - > last_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-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 )
{
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs ;
2010-05-13 18:48:03 +04:00
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
2015-03-24 23:09:39 +03:00
mrs = minstrel_get_ratestats ( mi , index ) ;
if ( mrs - > prob_ewma < MINSTREL_FRAC ( 1 , 10 ) ) {
mrs - > retry_count = 1 ;
mrs - > retry_count_rtscts = 1 ;
2010-05-13 18:48:03 +04:00
return ;
}
2015-03-24 23:09:39 +03:00
mrs - > retry_count = 2 ;
mrs - > retry_count_rtscts = 2 ;
mrs - > retry_updated = true ;
2010-05-13 18:48:03 +04:00
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 )
2015-03-24 23:09:39 +03:00
mrs - > retry_count_rtscts + + ;
2010-05-13 18:48:03 +04:00
} while ( ( tx_time < mp - > segment_size ) & &
2015-03-24 23:09:39 +03:00
( + + mrs - > retry_count < mp - > max_retry ) ) ;
2010-05-13 18:48:03 +04:00
}
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 ] ;
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs ;
2013-04-22 18:14:42 +04:00
u8 idx ;
2014-10-20 17:46:01 +04:00
u16 flags = group - > flags ;
2010-05-13 18:48:03 +04:00
2015-03-24 23:09:39 +03:00
mrs = minstrel_get_ratestats ( mi , index ) ;
if ( ! mrs - > retry_updated )
2010-05-13 18:48:03 +04:00
minstrel_calc_retransmit ( mp , mi , index ) ;
2015-03-24 23:09:39 +03:00
if ( mrs - > prob_ewma < MINSTREL_FRAC ( 20 , 100 ) | | ! mrs - > retry_count ) {
2013-04-22 18:14:42 +04:00
ratetbl - > rate [ offset ] . count = 2 ;
ratetbl - > rate [ offset ] . count_rts = 2 ;
ratetbl - > rate [ offset ] . count_cts = 2 ;
} else {
2015-03-24 23:09:39 +03:00
ratetbl - > rate [ offset ] . count = mrs - > retry_count ;
ratetbl - > rate [ offset ] . count_cts = mrs - > retry_count ;
ratetbl - > rate [ offset ] . count_rts = mrs - > retry_count_rtscts ;
2013-04-22 18:14:42 +04:00
}
2013-02-13 13:51:08 +04:00
2014-10-20 17:46:01 +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 ) ] ;
2014-10-21 12:38:38 +04:00
else if ( flags & IEEE80211_TX_RC_VHT_MCS )
idx = ( ( group - > streams - 1 ) < < 4 ) |
( ( index % MCS_GROUP_RATES ) & 0xF ) ;
2014-10-20 17:46:01 +04:00
else
2013-11-11 16:12:55 +04:00
idx = index % MCS_GROUP_RATES + ( group - > streams - 1 ) * 8 ;
2013-04-22 18:14:42 +04:00
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
2014-09-10 01:22:14 +04:00
/* Start with max_tp_rate[0] */
minstrel_ht_set_rate ( mp , mi , rates , i + + , mi - > max_tp_rate [ 0 ] ) ;
2013-04-22 18:14:42 +04:00
if ( mp - > hw - > max_rates > = 3 ) {
2014-09-10 01:22:14 +04:00
/* At least 3 tx rates supported, use max_tp_rate[1] next */
minstrel_ht_set_rate ( mp , mi , rates , i + + , mi - > max_tp_rate [ 1 ] ) ;
2013-04-22 18:14:42 +04:00
}
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 )
{
2015-03-24 23:09:39 +03:00
struct minstrel_rate_stats * mrs ;
2010-05-13 18:48:03 +04:00
struct minstrel_mcs_group_data * mg ;
2014-09-10 01:22:14 +04:00
unsigned int sample_dur , sample_group , cur_max_tp_streams ;
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 ;
2013-11-11 16:10:49 +04:00
sample_group = mi - > sample_group ;
mg = & mi - > groups [ sample_group ] ;
2010-05-13 18:48:03 +04:00
sample_idx = sample_table [ mg - > column ] [ mg - > index ] ;
2015-03-24 23:09:39 +03:00
minstrel_set_next_sample_idx ( mi ) ;
2013-11-11 16:10:49 +04:00
if ( ! ( mg - > supported & BIT ( sample_idx ) ) )
return - 1 ;
2015-03-24 23:09:39 +03:00
mrs = & mg - > rates [ sample_idx ] ;
2013-03-03 15:49:51 +04:00
sample_idx + = sample_group * MCS_GROUP_RATES ;
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
*/
2014-09-10 01:22:14 +04:00
if ( sample_idx = = mi - > max_tp_rate [ 0 ] | |
sample_idx = = mi - > max_tp_rate [ 1 ] | |
2013-03-16 20:00:27 +04:00
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
*/
2015-03-24 23:09:39 +03:00
if ( mrs - > prob_ewma > 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 .
*/
2014-09-10 01:22:14 +04:00
cur_max_tp_streams = minstrel_mcs_groups [ mi - > max_tp_rate [ 0 ] /
MCS_GROUP_RATES ] . streams ;
2013-03-03 15:49:51 +04:00
sample_dur = minstrel_get_duration ( sample_idx ) ;
2014-09-10 01:22:14 +04:00
if ( sample_dur > = minstrel_get_duration ( mi - > max_tp_rate [ 1 ] ) & &
( cur_max_tp_streams - 1 <
2013-03-03 15:49:51 +04:00
minstrel_mcs_groups [ sample_group ] . streams | |
sample_dur > = minstrel_get_duration ( mi - > max_prob_rate ) ) ) {
2015-03-24 23:09:39 +03:00
if ( mrs - > 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 ) ;
2014-11-19 22:08:07 +03:00
if ( ! ( info - > flags & IEEE80211_TX_CTL_AMPDU ) & &
mi - > max_prob_rate / MCS_GROUP_RATES ! = MINSTREL_CCK_GROUP )
minstrel_aggr_check ( sta , txrc - > skb ) ;
2010-05-13 18:48:03 +04:00
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
2013-08-27 18:59:47 +04:00
# ifdef CONFIG_MAC80211_DEBUGFS
if ( mp - > fixed_rate_idx ! = - 1 )
return ;
# endif
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
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 ;
2013-06-28 23:04:35 +04:00
rate - > count = 1 ;
if ( sample_idx / MCS_GROUP_RATES = = MINSTREL_CCK_GROUP ) {
int idx = sample_idx % ARRAY_SIZE ( mp - > cck_rates ) ;
rate - > idx = mp - > cck_rates [ idx ] ;
2014-10-21 12:38:38 +04:00
} else if ( sample_group - > flags & IEEE80211_TX_RC_VHT_MCS ) {
ieee80211_rate_set_vht ( rate , sample_idx % MCS_GROUP_RATES ,
sample_group - > streams ) ;
2014-10-20 17:46:01 +04:00
} else {
rate - > idx = sample_idx % MCS_GROUP_RATES +
( sample_group - > streams - 1 ) * 8 ;
2013-06-28 23:04:35 +04:00
}
2014-10-20 17:46:01 +04:00
rate - > flags = sample_group - > flags ;
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 ;
2013-08-20 21:43:54 +04:00
if ( ! ( mp - > hw - > flags & IEEE80211_HW_SUPPORTS_HT_CCK_RATES ) )
return ;
2013-02-13 13:51:08 +04:00
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 ;
2014-10-21 12:38:38 +04:00
struct ieee80211_sta_vht_cap * vht_cap = & sta - > vht_cap ;
int use_vht ;
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
2014-10-20 17:46:00 +04:00
BUILD_BUG_ON ( ARRAY_SIZE ( minstrel_mcs_groups ) ! = MINSTREL_GROUPS_NB ) ;
2010-05-13 18:48:03 +04:00
2014-10-21 12:38:38 +04:00
# ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
if ( vht_cap - > vht_supported )
use_vht = vht_cap - > vht_mcs . tx_mcs_map ! = cpu_to_le16 ( ~ 0 ) ;
else
# endif
use_vht = 0 ;
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 ;
2015-03-24 23:09:39 +03:00
mi - > last_stats_update = jiffies ;
2010-05-13 18:48:03 +04:00
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 ;
2014-10-21 12:38:38 +04:00
/* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
if ( ! use_vht ) {
stbc = ( sta_cap & IEEE80211_HT_CAP_RX_STBC ) > >
IEEE80211_HT_CAP_RX_STBC_SHIFT ;
mi - > tx_flags | = stbc < < IEEE80211_TX_CTL_STBC_SHIFT ;
2010-05-13 18:48:03 +04:00
2014-10-21 12:38:38 +04:00
if ( sta_cap & IEEE80211_HT_CAP_LDPC_CODING )
mi - > tx_flags | = IEEE80211_TX_CTL_LDPC ;
}
2010-05-13 18:48:03 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( mi - > groups ) ; i + + ) {
2014-10-20 17:46:01 +04:00
u32 gflags = minstrel_mcs_groups [ i ] . flags ;
2014-10-21 12:38:38 +04:00
int bw , nss ;
2014-10-20 17:46:01 +04:00
2010-05-13 18:48:03 +04:00
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 ;
}
2014-10-20 17:46:01 +04:00
if ( gflags & IEEE80211_TX_RC_SHORT_GI ) {
if ( gflags & IEEE80211_TX_RC_40_MHZ_WIDTH ) {
2013-02-07 14:47:44 +04:00
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
}
2014-10-20 17:46:01 +04:00
if ( gflags & IEEE80211_TX_RC_40_MHZ_WIDTH & &
2013-02-07 14:47:44 +04:00
sta - > bandwidth < IEEE80211_STA_RX_BW_40 )
2010-05-13 18:48:03 +04:00
continue ;
2014-10-21 12:38:38 +04:00
nss = minstrel_mcs_groups [ i ] . streams ;
2012-03-09 17:13:45 +04:00
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
2014-10-21 12:38:38 +04:00
if ( sta - > smps_mode = = IEEE80211_SMPS_STATIC & & nss > 1 )
continue ;
/* HT rate */
if ( gflags & IEEE80211_TX_RC_MCS ) {
# ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
2014-10-24 16:34:49 +04:00
if ( use_vht & & minstrel_vht_only )
2014-10-21 12:38:38 +04:00
continue ;
# endif
mi - > groups [ i ] . supported = mcs - > rx_mask [ nss - 1 ] ;
if ( mi - > groups [ i ] . supported )
n_supported + + ;
continue ;
}
/* VHT rate */
if ( ! vht_cap - > vht_supported | |
WARN_ON ( ! ( gflags & IEEE80211_TX_RC_VHT_MCS ) ) | |
WARN_ON ( gflags & IEEE80211_TX_RC_160_MHZ_WIDTH ) )
2012-03-09 17:13:45 +04:00
continue ;
2014-10-21 12:38:38 +04:00
if ( gflags & IEEE80211_TX_RC_80_MHZ_WIDTH ) {
if ( sta - > bandwidth < IEEE80211_STA_RX_BW_80 | |
( ( gflags & IEEE80211_TX_RC_SHORT_GI ) & &
! ( vht_cap - > cap & IEEE80211_VHT_CAP_SHORT_GI_80 ) ) ) {
continue ;
}
}
if ( gflags & IEEE80211_TX_RC_40_MHZ_WIDTH )
bw = BW_40 ;
else if ( gflags & IEEE80211_TX_RC_80_MHZ_WIDTH )
bw = BW_80 ;
else
bw = BW_20 ;
mi - > groups [ i ] . supported = minstrel_get_valid_vht_rates ( bw , nss ,
vht_cap - > vht_mcs . tx_mcs_map ) ;
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 ) ;
}
2014-05-19 23:53:20 +04:00
static u32 minstrel_ht_get_expected_throughput ( void * priv_sta )
{
struct minstrel_ht_sta_priv * msp = priv_sta ;
struct minstrel_ht_sta * mi = & msp - > ht ;
2015-03-24 23:09:40 +03:00
int i , j , tp_avg ;
2014-05-19 23:53:20 +04:00
if ( ! msp - > is_ht )
return mac80211_minstrel . get_expected_throughput ( priv_sta ) ;
2014-09-10 01:22:14 +04:00
i = mi - > max_tp_rate [ 0 ] / MCS_GROUP_RATES ;
j = mi - > max_tp_rate [ 0 ] % MCS_GROUP_RATES ;
2014-05-19 23:53:20 +04:00
2015-03-24 23:09:40 +03:00
/* convert tp_avg from pkt per second in kbps */
tp_avg = minstrel_ht_get_tp_avg ( mi , i , j ) * AVG_PKT_SIZE * 8 / 1024 ;
return tp_avg ;
2014-05-19 23:53:20 +04:00
}
2014-01-21 02:29:34 +04:00
static const struct rate_control_ops mac80211_minstrel_ht = {
2010-05-13 18:48:03 +04:00
. name = " minstrel_ht " ,
2014-11-19 22:08:10 +03:00
. tx_status_noskb = minstrel_ht_tx_status ,
2010-05-13 18:48:03 +04:00
. 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
2014-05-19 23:53:20 +04:00
. get_expected_throughput = minstrel_ht_get_expected_throughput ,
2010-05-13 18:48:03 +04:00
} ;
2014-01-21 03:32:52 +04:00
static void __init init_sample_table ( void )
2010-05-13 18:48:03 +04:00
{
int col , i , new_idx ;
u8 rnd [ MCS_GROUP_RATES ] ;
memset ( sample_table , 0xff , sizeof ( sample_table ) ) ;
for ( col = 0 ; col < SAMPLE_COLUMNS ; col + + ) {
2013-11-13 13:54:19 +04:00
prandom_bytes ( rnd , sizeof ( rnd ) ) ;
2010-05-13 18:48:03 +04:00
for ( i = 0 ; i < MCS_GROUP_RATES ; i + + ) {
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 ) ;
}