Commit b7ffbd7e authored by Johannes Berg's avatar Johannes Berg

cfg80211: make ethtool the driver's responsibility

Currently, cfg80211 tries to implement ethtool, but that doesn't
really scale well, with all the different operations. Make the
lower-level driver responsible for it, which currently only has
an effect on mac80211. It will similarly not scale well at that
level though, since mac80211 also has many drivers.

To cleanly implement this in mac80211, introduce a new file and
move some code to appropriate places.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent ba9030c2
......@@ -2266,10 +2266,6 @@ struct cfg80211_qos_map {
*
* @get_antenna: Get current antenna configuration from device (tx_ant, rx_ant).
*
* @set_ringparam: Set tx and rx ring sizes.
*
* @get_ringparam: Get tx and rx ring current and maximum sizes.
*
* @tdls_mgmt: Transmit a TDLS management frame.
* @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup).
*
......@@ -2278,16 +2274,6 @@ struct cfg80211_qos_map {
*
* @set_noack_map: Set the NoAck Map for the TIDs.
*
* @get_et_sset_count: Ethtool API to get string-set count.
* See @ethtool_ops.get_sset_count
*
* @get_et_stats: Ethtool API to get a set of u64 stats.
* See @ethtool_ops.get_ethtool_stats
*
* @get_et_strings: Ethtool API to get a set of strings to describe stats
* and perhaps other supported types of ethtool data-sets.
* See @ethtool_ops.get_strings
*
* @get_channel: Get the current operating channel for the virtual interface.
* For monitor interfaces, it should return %NULL unless there's a single
* current monitoring channel.
......@@ -2503,10 +2489,6 @@ struct cfg80211_ops {
int (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
int (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
int (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
void (*get_ringparam)(struct wiphy *wiphy,
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
int (*sched_scan_start)(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_sched_scan_request *request);
......@@ -2529,13 +2511,6 @@ struct cfg80211_ops {
struct net_device *dev,
u16 noack_map);
int (*get_et_sset_count)(struct wiphy *wiphy,
struct net_device *dev, int sset);
void (*get_et_stats)(struct wiphy *wiphy, struct net_device *dev,
struct ethtool_stats *stats, u64 *data);
void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev,
u32 sset, u8 *data);
int (*get_channel)(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
......@@ -4843,6 +4818,10 @@ void cfg80211_stop_iface(struct wiphy *wiphy, struct wireless_dev *wdev,
*/
void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
/* ethtool helper */
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
/* Logging, debugging and troubleshooting/diagnostic helpers. */
/* wiphy_printk helpers, similar to dev_printk */
......
......@@ -17,6 +17,7 @@ mac80211-y := \
aes_ccm.o \
aes_cmac.o \
cfg.o \
ethtool.o \
rx.o \
spectmgmt.o \
tx.o \
......
......@@ -468,326 +468,6 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
}
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
struct rate_control_ref *ref = local->rate_ctrl;
struct timespec uptime;
u64 packets = 0;
u32 thr = 0;
int i, ac;
sinfo->generation = sdata->local->sta_generation;
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES64 |
STATION_INFO_TX_BYTES64 |
STATION_INFO_RX_PACKETS |
STATION_INFO_TX_PACKETS |
STATION_INFO_TX_RETRIES |
STATION_INFO_TX_FAILED |
STATION_INFO_TX_BITRATE |
STATION_INFO_RX_BITRATE |
STATION_INFO_RX_DROP_MISC |
STATION_INFO_BSS_PARAM |
STATION_INFO_CONNECTED_TIME |
STATION_INFO_STA_FLAGS |
STATION_INFO_BEACON_LOSS_COUNT;
do_posix_clock_monotonic_gettime(&uptime);
sinfo->connected_time = uptime.tv_sec - sta->last_connected;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->tx_bytes = 0;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
sinfo->tx_bytes += sta->tx_bytes[ac];
packets += sta->tx_packets[ac];
}
sinfo->tx_packets = packets;
sinfo->rx_bytes = sta->rx_bytes;
sinfo->rx_packets = sta->rx_packets;
sinfo->tx_retries = sta->tx_retry_count;
sinfo->tx_failed = sta->tx_retry_failed;
sinfo->rx_dropped_misc = sta->rx_dropped;
sinfo->beacon_loss_count = sta->beacon_loss_count;
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG;
if (!local->ops->get_rssi ||
drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
sinfo->signal = (s8)sta->last_signal;
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
}
if (sta->chains) {
sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
STATION_INFO_CHAIN_SIGNAL_AVG;
sinfo->chains = sta->chains;
for (i = 0; i < ARRAY_SIZE(sinfo->chain_signal); i++) {
sinfo->chain_signal[i] = sta->chain_signal_last[i];
sinfo->chain_signal_avg[i] =
(s8) -ewma_read(&sta->chain_signal_avg[i]);
}
}
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
sta_set_rate_info_rx(sta, &sinfo->rxrate);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
sinfo->filled |= STATION_INFO_LLID |
STATION_INFO_PLID |
STATION_INFO_PLINK_STATE |
STATION_INFO_LOCAL_PM |
STATION_INFO_PEER_PM |
STATION_INFO_NONPEER_PM;
sinfo->llid = sta->llid;
sinfo->plid = sta->plid;
sinfo->plink_state = sta->plink_state;
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
sinfo->filled |= STATION_INFO_T_OFFSET;
sinfo->t_offset = sta->t_offset;
}
sinfo->local_pm = sta->local_pm;
sinfo->peer_pm = sta->peer_pm;
sinfo->nonpeer_pm = sta->nonpeer_pm;
#endif
}
sinfo->bss_param.flags = 0;
if (sdata->vif.bss_conf.use_cts_prot)
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
if (sdata->vif.bss_conf.use_short_preamble)
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
if (sdata->vif.bss_conf.use_short_slot)
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
sinfo->bss_param.dtim_period = sdata->local->hw.conf.ps_dtim_period;
sinfo->bss_param.beacon_interval = sdata->vif.bss_conf.beacon_int;
sinfo->sta_flags.set = 0;
sinfo->sta_flags.mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_MFP) |
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_TDLS_PEER);
if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
if (test_sta_flag(sta, WLAN_STA_WME))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_WME);
if (test_sta_flag(sta, WLAN_STA_MFP))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP);
if (test_sta_flag(sta, WLAN_STA_AUTH))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
if (test_sta_flag(sta, WLAN_STA_ASSOC))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
/* check if the driver has a SW RC implementation */
if (ref && ref->ops->get_expected_throughput)
thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
else
thr = drv_get_expected_throughput(local, &sta->sta);
if (thr != 0) {
sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
sinfo->expected_throughput = thr;
}
}
static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
"rx_packets", "rx_bytes",
"rx_duplicates", "rx_fragments", "rx_dropped",
"tx_packets", "tx_bytes", "tx_fragments",
"tx_filtered", "tx_retry_failed", "tx_retries",
"beacon_loss", "sta_state", "txrate", "rxrate", "signal",
"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_et_sset_count(struct wiphy *wiphy,
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_et_stats(struct wiphy *wiphy,
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;
struct station_info sinfo;
struct survey_info survey;
int i, q;
#define STA_STATS_SURVEY_LEN 7
memset(data, 0, sizeof(u64) * STA_STATS_LEN);
#define ADD_STA_STATS(sta) \
do { \
data[i++] += sta->rx_packets; \
data[i++] += sta->rx_bytes; \
data[i++] += sta->num_duplicates; \
data[i++] += sta->rx_fragments; \
data[i++] += sta->rx_dropped; \
\
data[i++] += sinfo.tx_packets; \
data[i++] += sinfo.tx_bytes; \
data[i++] += sta->tx_fragments; \
data[i++] += sta->tx_filtered_count; \
data[i++] += sta->tx_retry_failed; \
data[i++] += sta->tx_retry_count; \
data[i++] += sta->beacon_loss_count; \
} 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;
sinfo.filled = 0;
sta_set_sinfo(sta, &sinfo);
i = 0;
ADD_STA_STATS(sta);
data[i++] = sta->sta_state;
if (sinfo.filled & STATION_INFO_TX_BITRATE)
data[i] = 100000 *
cfg80211_calculate_bitrate(&sinfo.txrate);
i++;
if (sinfo.filled & STATION_INFO_RX_BITRATE)
data[i] = 100000 *
cfg80211_calculate_bitrate(&sinfo.rxrate);
i++;
if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
data[i] = (u8)sinfo.signal_avg;
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;
sinfo.filled = 0;
sta_set_sinfo(sta, &sinfo);
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;
if (survey.filled & SURVEY_INFO_CHANNEL_TIME)
data[i++] = survey.channel_time;
else
data[i++] = -1LL;
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
data[i++] = survey.channel_time_busy;
else
data[i++] = -1LL;
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
data[i++] = survey.channel_time_ext_busy;
else
data[i++] = -1LL;
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX)
data[i++] = survey.channel_time_rx;
else
data[i++] = -1LL;
if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX)
data[i++] = survey.channel_time_tx;
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_et_strings(struct wiphy *wiphy,
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_dump_station(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo)
{
......@@ -3597,21 +3277,6 @@ static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
return drv_get_antenna(local, tx_ant, rx_ant);
}
static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
return drv_set_ringparam(local, tx, rx);
}
static void ieee80211_get_ringparam(struct wiphy *wiphy,
u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
drv_get_ringparam(local, tx, tx_max, rx, rx_max);
}
static int ieee80211_set_rekey_data(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_gtk_rekey_data *data)
......@@ -3843,8 +3508,6 @@ const struct cfg80211_ops mac80211_config_ops = {
.mgmt_frame_register = ieee80211_mgmt_frame_register,
.set_antenna = ieee80211_set_antenna,
.get_antenna = ieee80211_get_antenna,
.set_ringparam = ieee80211_set_ringparam,
.get_ringparam = ieee80211_get_ringparam,
.set_rekey_data = ieee80211_set_rekey_data,
.tdls_oper = ieee80211_tdls_oper,
.tdls_mgmt = ieee80211_tdls_mgmt,
......@@ -3853,9 +3516,6 @@ const struct cfg80211_ops mac80211_config_ops = {
#ifdef CONFIG_PM
.set_wakeup = ieee80211_set_wakeup,
#endif
.get_et_sset_count = ieee80211_get_et_sset_count,
.get_et_stats = ieee80211_get_et_stats,
.get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
.start_radar_detection = ieee80211_start_radar_detection,
.channel_switch = ieee80211_channel_switch,
......
/*
* 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)
*
* 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",
"tx_packets", "tx_bytes", "tx_fragments",
"tx_filtered", "tx_retry_failed", "tx_retries",
"beacon_loss", "sta_state", "txrate", "rxrate", "signal",
"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;
struct station_info sinfo;
struct survey_info survey;
int i, q;
#define STA_STATS_SURVEY_LEN 7
memset(data, 0, sizeof(u64) * STA_STATS_LEN);
#define ADD_STA_STATS(sta) \
do { \
data[i++] += sta->rx_packets; \
data[i++] += sta->rx_bytes; \
data[i++] += sta->num_duplicates; \
data[i++] += sta->rx_fragments; \
data[i++] += sta->rx_dropped; \
\
data[i++] += sinfo.tx_packets; \
data[i++] += sinfo.tx_bytes; \
data[i++] += sta->tx_fragments; \
data[i++] += sta->tx_filtered_count; \
data[i++] += sta->tx_retry_failed; \
data[i++] += sta->tx_retry_count; \
data[i++] += sta->beacon_loss_count; \
} 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;
sinfo.filled = 0;
sta_set_sinfo(sta, &sinfo);
i = 0;
ADD_STA_STATS(sta);
data[i++] = sta->sta_state;
if (sinfo.filled & STATION_INFO_TX_BITRATE)
data[i] = 100000 *
cfg80211_calculate_bitrate(&sinfo.txrate);
i++;
if (sinfo.filled & STATION_INFO_RX_BITRATE)
data[i] = 100000 *
cfg80211_calculate_bitrate(&sinfo.rxrate);
i++;
if (sinfo.filled & STATION_INFO_SIGNAL_AVG)
data[i] = (u8)sinfo.signal_avg;
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;
sinfo.filled = 0;
sta_set_sinfo(sta, &sinfo);
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;