2014-06-04 19:31:56 +04:00
/*
* mac80211 ethtool hooks for cfg80211
*
* Copied from cfg . c - originally
* Copyright 2006 - 2010 Johannes Berg < johannes @ sipsolutions . net >
* Copyright 2014 Intel Corporation ( Author : Johannes Berg )
2018-04-20 13:49:22 +03:00
* Copyright ( C ) 2018 Intel Corporation
2014-06-04 19:31:56 +04:00
*
* This file is GPLv2 as found in COPYING .
*/
# include <linux/types.h>
# include <net/cfg80211.h>
# include "ieee80211_i.h"
# include "sta_info.h"
# include "driver-ops.h"
static int ieee80211_set_ringparam ( struct net_device * dev ,
struct ethtool_ringparam * rp )
{
struct ieee80211_local * local = wiphy_priv ( dev - > ieee80211_ptr - > wiphy ) ;
if ( rp - > rx_mini_pending ! = 0 | | rp - > rx_jumbo_pending ! = 0 )
return - EINVAL ;
return drv_set_ringparam ( local , rp - > tx_pending , rp - > rx_pending ) ;
}
static void ieee80211_get_ringparam ( struct net_device * dev ,
struct ethtool_ringparam * rp )
{
struct ieee80211_local * local = wiphy_priv ( dev - > ieee80211_ptr - > wiphy ) ;
memset ( rp , 0 , sizeof ( * rp ) ) ;
drv_get_ringparam ( local , & rp - > tx_pending , & rp - > tx_max_pending ,
& rp - > rx_pending , & rp - > rx_max_pending ) ;
}
static const char ieee80211_gstrings_sta_stats [ ] [ ETH_GSTRING_LEN ] = {
" rx_packets " , " rx_bytes " ,
" rx_duplicates " , " rx_fragments " , " rx_dropped " ,
2015-04-22 21:55:55 +03:00
" tx_packets " , " tx_bytes " ,
2014-06-04 19:31:56 +04:00
" tx_filtered " , " tx_retry_failed " , " tx_retries " ,
2015-10-16 18:18:11 +03:00
" sta_state " , " txrate " , " rxrate " , " signal " ,
2014-06-04 19:31:56 +04:00
" channel " , " noise " , " ch_time " , " ch_time_busy " ,
" ch_time_ext_busy " , " ch_time_rx " , " ch_time_tx "
} ;
# define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats)
static int ieee80211_get_sset_count ( struct net_device * dev , int sset )
{
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
int rv = 0 ;
if ( sset = = ETH_SS_STATS )
rv + = STA_STATS_LEN ;
rv + = drv_get_et_sset_count ( sdata , sset ) ;
if ( rv = = 0 )
return - EOPNOTSUPP ;
return rv ;
}
static void ieee80211_get_stats ( struct net_device * dev ,
struct ethtool_stats * stats ,
u64 * data )
{
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
struct ieee80211_chanctx_conf * chanctx_conf ;
struct ieee80211_channel * channel ;
struct sta_info * sta ;
struct ieee80211_local * local = sdata - > local ;
2018-05-18 10:57:55 +03:00
struct station_info sinfo ;
2014-06-04 19:31:56 +04:00
struct survey_info survey ;
int i , q ;
# define STA_STATS_SURVEY_LEN 7
memset ( data , 0 , sizeof ( u64 ) * STA_STATS_LEN ) ;
2015-10-16 18:54:47 +03:00
# define ADD_STA_STATS(sta) \
do { \
data [ i + + ] + = sta - > rx_stats . packets ; \
data [ i + + ] + = sta - > rx_stats . bytes ; \
data [ i + + ] + = sta - > rx_stats . num_duplicates ; \
data [ i + + ] + = sta - > rx_stats . fragments ; \
data [ i + + ] + = sta - > rx_stats . dropped ; \
\
2018-05-18 10:57:55 +03:00
data [ i + + ] + = sinfo . tx_packets ; \
data [ i + + ] + = sinfo . tx_bytes ; \
2015-10-16 18:54:47 +03:00
data [ i + + ] + = sta - > status_stats . filtered ; \
data [ i + + ] + = sta - > status_stats . retry_failed ; \
data [ i + + ] + = sta - > status_stats . retry_count ; \
2014-06-04 19:31:56 +04:00
} while ( 0 )
/* For Managed stations, find the single station based on BSSID
* and use that . For interface types , iterate through all available
* stations and add stats for any station that is assigned to this
* network device .
*/
mutex_lock ( & local - > sta_mtx ) ;
if ( sdata - > vif . type = = NL80211_IFTYPE_STATION ) {
sta = sta_info_get_bss ( sdata , sdata - > u . mgd . bssid ) ;
if ( ! ( sta & & ! WARN_ON ( sta - > sdata - > dev ! = dev ) ) )
goto do_survey ;
2018-05-18 10:57:55 +03:00
memset ( & sinfo , 0 , sizeof ( sinfo ) ) ;
2018-05-18 12:40:44 +03:00
sta_set_sinfo ( sta , & sinfo , false ) ;
2014-06-04 19:31:56 +04:00
i = 0 ;
ADD_STA_STATS ( sta ) ;
data [ i + + ] = sta - > sta_state ;
2018-05-18 10:57:55 +03:00
if ( sinfo . filled & BIT ( NL80211_STA_INFO_TX_BITRATE ) )
2018-05-08 15:57:32 +03:00
data [ i ] = 100000ULL *
2018-05-18 10:57:55 +03:00
cfg80211_calculate_bitrate ( & sinfo . txrate ) ;
2014-06-04 19:31:56 +04:00
i + + ;
2018-05-18 10:57:55 +03:00
if ( sinfo . filled & BIT ( NL80211_STA_INFO_RX_BITRATE ) )
2018-05-08 15:57:32 +03:00
data [ i ] = 100000ULL *
2018-05-18 10:57:55 +03:00
cfg80211_calculate_bitrate ( & sinfo . rxrate ) ;
2014-06-04 19:31:56 +04:00
i + + ;
2018-05-18 10:57:55 +03:00
if ( sinfo . filled & BIT ( NL80211_STA_INFO_SIGNAL_AVG ) )
data [ i ] = ( u8 ) sinfo . signal_avg ;
2014-06-04 19:31:56 +04:00
i + + ;
} else {
list_for_each_entry ( sta , & local - > sta_list , list ) {
/* Make sure this station belongs to the proper dev */
if ( sta - > sdata - > dev ! = dev )
continue ;
2018-05-18 10:57:55 +03:00
memset ( & sinfo , 0 , sizeof ( sinfo ) ) ;
2018-05-18 12:40:44 +03:00
sta_set_sinfo ( sta , & sinfo , false ) ;
2014-06-04 19:31:56 +04:00
i = 0 ;
ADD_STA_STATS ( sta ) ;
}
}
do_survey :
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN ;
/* Get survey stats for current channel */
survey . filled = 0 ;
rcu_read_lock ( ) ;
chanctx_conf = rcu_dereference ( sdata - > vif . chanctx_conf ) ;
if ( chanctx_conf )
channel = chanctx_conf - > def . chan ;
else
channel = NULL ;
rcu_read_unlock ( ) ;
if ( channel ) {
q = 0 ;
do {
survey . filled = 0 ;
if ( drv_get_survey ( local , q , & survey ) ! = 0 ) {
survey . filled = 0 ;
break ;
}
q + + ;
} while ( channel ! = survey . channel ) ;
}
if ( survey . filled )
data [ i + + ] = survey . channel - > center_freq ;
else
data [ i + + ] = 0 ;
if ( survey . filled & SURVEY_INFO_NOISE_DBM )
data [ i + + ] = ( u8 ) survey . noise ;
else
data [ i + + ] = - 1LL ;
2014-11-14 18:35:34 +03:00
if ( survey . filled & SURVEY_INFO_TIME )
data [ i + + ] = survey . time ;
2014-06-04 19:31:56 +04:00
else
data [ i + + ] = - 1LL ;
2014-11-14 18:35:34 +03:00
if ( survey . filled & SURVEY_INFO_TIME_BUSY )
data [ i + + ] = survey . time_busy ;
2014-06-04 19:31:56 +04:00
else
data [ i + + ] = - 1LL ;
2014-11-14 18:35:34 +03:00
if ( survey . filled & SURVEY_INFO_TIME_EXT_BUSY )
data [ i + + ] = survey . time_ext_busy ;
2014-06-04 19:31:56 +04:00
else
data [ i + + ] = - 1LL ;
2014-11-14 18:35:34 +03:00
if ( survey . filled & SURVEY_INFO_TIME_RX )
data [ i + + ] = survey . time_rx ;
2014-06-04 19:31:56 +04:00
else
data [ i + + ] = - 1LL ;
2014-11-14 18:35:34 +03:00
if ( survey . filled & SURVEY_INFO_TIME_TX )
data [ i + + ] = survey . time_tx ;
2014-06-04 19:31:56 +04:00
else
data [ i + + ] = - 1LL ;
mutex_unlock ( & local - > sta_mtx ) ;
if ( WARN_ON ( i ! = STA_STATS_LEN ) )
return ;
drv_get_et_stats ( sdata , stats , & ( data [ STA_STATS_LEN ] ) ) ;
}
static void ieee80211_get_strings ( struct net_device * dev , u32 sset , u8 * data )
{
struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF ( dev ) ;
int sz_sta_stats = 0 ;
if ( sset = = ETH_SS_STATS ) {
sz_sta_stats = sizeof ( ieee80211_gstrings_sta_stats ) ;
memcpy ( data , ieee80211_gstrings_sta_stats , sz_sta_stats ) ;
}
drv_get_et_strings ( sdata , sset , & ( data [ sz_sta_stats ] ) ) ;
}
static int ieee80211_get_regs_len ( struct net_device * dev )
{
return 0 ;
}
static void ieee80211_get_regs ( struct net_device * dev ,
struct ethtool_regs * regs ,
void * data )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
regs - > version = wdev - > wiphy - > hw_version ;
regs - > len = 0 ;
}
const struct ethtool_ops ieee80211_ethtool_ops = {
. get_drvinfo = cfg80211_get_drvinfo ,
. get_regs_len = ieee80211_get_regs_len ,
. get_regs = ieee80211_get_regs ,
. get_link = ethtool_op_get_link ,
. get_ringparam = ieee80211_get_ringparam ,
. set_ringparam = ieee80211_set_ringparam ,
. get_strings = ieee80211_get_strings ,
. get_ethtool_stats = ieee80211_get_stats ,
. get_sset_count = ieee80211_get_sset_count ,
} ;