162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mac80211 ethtool hooks for cfg80211 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copied from cfg.c - originally 662306a36Sopenharmony_ci * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 762306a36Sopenharmony_ci * Copyright 2014 Intel Corporation (Author: Johannes Berg) 862306a36Sopenharmony_ci * Copyright (C) 2018, 2022 Intel Corporation 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <net/cfg80211.h> 1262306a36Sopenharmony_ci#include "ieee80211_i.h" 1362306a36Sopenharmony_ci#include "sta_info.h" 1462306a36Sopenharmony_ci#include "driver-ops.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int ieee80211_set_ringparam(struct net_device *dev, 1762306a36Sopenharmony_ci struct ethtool_ringparam *rp, 1862306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_rp, 1962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) 2462306a36Sopenharmony_ci return -EINVAL; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void ieee80211_get_ringparam(struct net_device *dev, 3062306a36Sopenharmony_ci struct ethtool_ringparam *rp, 3162306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_rp, 3262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci memset(rp, 0, sizeof(*rp)); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending, 3962306a36Sopenharmony_ci &rp->rx_pending, &rp->rx_max_pending); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { 4362306a36Sopenharmony_ci "rx_packets", "rx_bytes", 4462306a36Sopenharmony_ci "rx_duplicates", "rx_fragments", "rx_dropped", 4562306a36Sopenharmony_ci "tx_packets", "tx_bytes", 4662306a36Sopenharmony_ci "tx_filtered", "tx_retry_failed", "tx_retries", 4762306a36Sopenharmony_ci "sta_state", "txrate", "rxrate", "signal", 4862306a36Sopenharmony_ci "channel", "noise", "ch_time", "ch_time_busy", 4962306a36Sopenharmony_ci "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int ieee80211_get_sset_count(struct net_device *dev, int sset) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 5662306a36Sopenharmony_ci int rv = 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (sset == ETH_SS_STATS) 5962306a36Sopenharmony_ci rv += STA_STATS_LEN; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci rv += drv_get_et_sset_count(sdata, sset); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci if (rv == 0) 6462306a36Sopenharmony_ci return -EOPNOTSUPP; 6562306a36Sopenharmony_ci return rv; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void ieee80211_get_stats(struct net_device *dev, 6962306a36Sopenharmony_ci struct ethtool_stats *stats, 7062306a36Sopenharmony_ci u64 *data) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 7362306a36Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 7462306a36Sopenharmony_ci struct ieee80211_channel *channel; 7562306a36Sopenharmony_ci struct sta_info *sta; 7662306a36Sopenharmony_ci struct ieee80211_local *local = sdata->local; 7762306a36Sopenharmony_ci struct station_info sinfo; 7862306a36Sopenharmony_ci struct survey_info survey; 7962306a36Sopenharmony_ci int i, q; 8062306a36Sopenharmony_ci#define STA_STATS_SURVEY_LEN 7 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci memset(data, 0, sizeof(u64) * STA_STATS_LEN); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define ADD_STA_STATS(sta) \ 8562306a36Sopenharmony_ci do { \ 8662306a36Sopenharmony_ci data[i++] += sinfo.rx_packets; \ 8762306a36Sopenharmony_ci data[i++] += sinfo.rx_bytes; \ 8862306a36Sopenharmony_ci data[i++] += (sta)->rx_stats.num_duplicates; \ 8962306a36Sopenharmony_ci data[i++] += (sta)->rx_stats.fragments; \ 9062306a36Sopenharmony_ci data[i++] += sinfo.rx_dropped_misc; \ 9162306a36Sopenharmony_ci \ 9262306a36Sopenharmony_ci data[i++] += sinfo.tx_packets; \ 9362306a36Sopenharmony_ci data[i++] += sinfo.tx_bytes; \ 9462306a36Sopenharmony_ci data[i++] += (sta)->status_stats.filtered; \ 9562306a36Sopenharmony_ci data[i++] += sinfo.tx_failed; \ 9662306a36Sopenharmony_ci data[i++] += sinfo.tx_retries; \ 9762306a36Sopenharmony_ci } while (0) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* For Managed stations, find the single station based on BSSID 10062306a36Sopenharmony_ci * and use that. For interface types, iterate through all available 10162306a36Sopenharmony_ci * stations and add stats for any station that is assigned to this 10262306a36Sopenharmony_ci * network device. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci mutex_lock(&local->sta_mtx); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) { 10862306a36Sopenharmony_ci sta = sta_info_get_bss(sdata, sdata->deflink.u.mgd.bssid); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!(sta && !WARN_ON(sta->sdata->dev != dev))) 11162306a36Sopenharmony_ci goto do_survey; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci memset(&sinfo, 0, sizeof(sinfo)); 11462306a36Sopenharmony_ci sta_set_sinfo(sta, &sinfo, false); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci i = 0; 11762306a36Sopenharmony_ci ADD_STA_STATS(&sta->deflink); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci data[i++] = sta->sta_state; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) 12362306a36Sopenharmony_ci data[i] = 100000ULL * 12462306a36Sopenharmony_ci cfg80211_calculate_bitrate(&sinfo.txrate); 12562306a36Sopenharmony_ci i++; 12662306a36Sopenharmony_ci if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) 12762306a36Sopenharmony_ci data[i] = 100000ULL * 12862306a36Sopenharmony_ci cfg80211_calculate_bitrate(&sinfo.rxrate); 12962306a36Sopenharmony_ci i++; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG)) 13262306a36Sopenharmony_ci data[i] = (u8)sinfo.signal_avg; 13362306a36Sopenharmony_ci i++; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 13662306a36Sopenharmony_ci /* Make sure this station belongs to the proper dev */ 13762306a36Sopenharmony_ci if (sta->sdata->dev != dev) 13862306a36Sopenharmony_ci continue; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci memset(&sinfo, 0, sizeof(sinfo)); 14162306a36Sopenharmony_ci sta_set_sinfo(sta, &sinfo, false); 14262306a36Sopenharmony_ci i = 0; 14362306a36Sopenharmony_ci ADD_STA_STATS(&sta->deflink); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cido_survey: 14862306a36Sopenharmony_ci i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; 14962306a36Sopenharmony_ci /* Get survey stats for current channel */ 15062306a36Sopenharmony_ci survey.filled = 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci rcu_read_lock(); 15362306a36Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf); 15462306a36Sopenharmony_ci if (chanctx_conf) 15562306a36Sopenharmony_ci channel = chanctx_conf->def.chan; 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci channel = NULL; 15862306a36Sopenharmony_ci rcu_read_unlock(); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (channel) { 16162306a36Sopenharmony_ci q = 0; 16262306a36Sopenharmony_ci do { 16362306a36Sopenharmony_ci survey.filled = 0; 16462306a36Sopenharmony_ci if (drv_get_survey(local, q, &survey) != 0) { 16562306a36Sopenharmony_ci survey.filled = 0; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci q++; 16962306a36Sopenharmony_ci } while (channel != survey.channel); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (survey.filled) 17362306a36Sopenharmony_ci data[i++] = survey.channel->center_freq; 17462306a36Sopenharmony_ci else 17562306a36Sopenharmony_ci data[i++] = 0; 17662306a36Sopenharmony_ci if (survey.filled & SURVEY_INFO_NOISE_DBM) 17762306a36Sopenharmony_ci data[i++] = (u8)survey.noise; 17862306a36Sopenharmony_ci else 17962306a36Sopenharmony_ci data[i++] = -1LL; 18062306a36Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME) 18162306a36Sopenharmony_ci data[i++] = survey.time; 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci data[i++] = -1LL; 18462306a36Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_BUSY) 18562306a36Sopenharmony_ci data[i++] = survey.time_busy; 18662306a36Sopenharmony_ci else 18762306a36Sopenharmony_ci data[i++] = -1LL; 18862306a36Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_EXT_BUSY) 18962306a36Sopenharmony_ci data[i++] = survey.time_ext_busy; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci data[i++] = -1LL; 19262306a36Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_RX) 19362306a36Sopenharmony_ci data[i++] = survey.time_rx; 19462306a36Sopenharmony_ci else 19562306a36Sopenharmony_ci data[i++] = -1LL; 19662306a36Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_TX) 19762306a36Sopenharmony_ci data[i++] = survey.time_tx; 19862306a36Sopenharmony_ci else 19962306a36Sopenharmony_ci data[i++] = -1LL; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci mutex_unlock(&local->sta_mtx); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (WARN_ON(i != STA_STATS_LEN)) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 21262306a36Sopenharmony_ci int sz_sta_stats = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (sset == ETH_SS_STATS) { 21562306a36Sopenharmony_ci sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); 21662306a36Sopenharmony_ci memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int ieee80211_get_regs_len(struct net_device *dev) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void ieee80211_get_regs(struct net_device *dev, 22762306a36Sopenharmony_ci struct ethtool_regs *regs, 22862306a36Sopenharmony_ci void *data) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci regs->version = wdev->wiphy->hw_version; 23362306a36Sopenharmony_ci regs->len = 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ciconst struct ethtool_ops ieee80211_ethtool_ops = { 23762306a36Sopenharmony_ci .get_drvinfo = cfg80211_get_drvinfo, 23862306a36Sopenharmony_ci .get_regs_len = ieee80211_get_regs_len, 23962306a36Sopenharmony_ci .get_regs = ieee80211_get_regs, 24062306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 24162306a36Sopenharmony_ci .get_ringparam = ieee80211_get_ringparam, 24262306a36Sopenharmony_ci .set_ringparam = ieee80211_set_ringparam, 24362306a36Sopenharmony_ci .get_strings = ieee80211_get_strings, 24462306a36Sopenharmony_ci .get_ethtool_stats = ieee80211_get_stats, 24562306a36Sopenharmony_ci .get_sset_count = ieee80211_get_sset_count, 24662306a36Sopenharmony_ci}; 247