18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mac80211 ethtool hooks for cfg80211 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copied from cfg.c - originally 68c2ecf20Sopenharmony_ci * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 78c2ecf20Sopenharmony_ci * Copyright 2014 Intel Corporation (Author: Johannes Berg) 88c2ecf20Sopenharmony_ci * Copyright (C) 2018 Intel Corporation 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <net/cfg80211.h> 128c2ecf20Sopenharmony_ci#include "ieee80211_i.h" 138c2ecf20Sopenharmony_ci#include "sta_info.h" 148c2ecf20Sopenharmony_ci#include "driver-ops.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int ieee80211_set_ringparam(struct net_device *dev, 178c2ecf20Sopenharmony_ci struct ethtool_ringparam *rp) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) 228c2ecf20Sopenharmony_ci return -EINVAL; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void ieee80211_get_ringparam(struct net_device *dev, 288c2ecf20Sopenharmony_ci struct ethtool_ringparam *rp) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci memset(rp, 0, sizeof(*rp)); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending, 358c2ecf20Sopenharmony_ci &rp->rx_pending, &rp->rx_max_pending); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { 398c2ecf20Sopenharmony_ci "rx_packets", "rx_bytes", 408c2ecf20Sopenharmony_ci "rx_duplicates", "rx_fragments", "rx_dropped", 418c2ecf20Sopenharmony_ci "tx_packets", "tx_bytes", 428c2ecf20Sopenharmony_ci "tx_filtered", "tx_retry_failed", "tx_retries", 438c2ecf20Sopenharmony_ci "sta_state", "txrate", "rxrate", "signal", 448c2ecf20Sopenharmony_ci "channel", "noise", "ch_time", "ch_time_busy", 458c2ecf20Sopenharmony_ci "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci#define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int ieee80211_get_sset_count(struct net_device *dev, int sset) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 528c2ecf20Sopenharmony_ci int rv = 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) 558c2ecf20Sopenharmony_ci rv += STA_STATS_LEN; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci rv += drv_get_et_sset_count(sdata, sset); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (rv == 0) 608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 618c2ecf20Sopenharmony_ci return rv; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void ieee80211_get_stats(struct net_device *dev, 658c2ecf20Sopenharmony_ci struct ethtool_stats *stats, 668c2ecf20Sopenharmony_ci u64 *data) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 698c2ecf20Sopenharmony_ci struct ieee80211_chanctx_conf *chanctx_conf; 708c2ecf20Sopenharmony_ci struct ieee80211_channel *channel; 718c2ecf20Sopenharmony_ci struct sta_info *sta; 728c2ecf20Sopenharmony_ci struct ieee80211_local *local = sdata->local; 738c2ecf20Sopenharmony_ci struct station_info sinfo; 748c2ecf20Sopenharmony_ci struct survey_info survey; 758c2ecf20Sopenharmony_ci int i, q; 768c2ecf20Sopenharmony_ci#define STA_STATS_SURVEY_LEN 7 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci memset(data, 0, sizeof(u64) * STA_STATS_LEN); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define ADD_STA_STATS(sta) \ 818c2ecf20Sopenharmony_ci do { \ 828c2ecf20Sopenharmony_ci data[i++] += sta->rx_stats.packets; \ 838c2ecf20Sopenharmony_ci data[i++] += sta->rx_stats.bytes; \ 848c2ecf20Sopenharmony_ci data[i++] += sta->rx_stats.num_duplicates; \ 858c2ecf20Sopenharmony_ci data[i++] += sta->rx_stats.fragments; \ 868c2ecf20Sopenharmony_ci data[i++] += sta->rx_stats.dropped; \ 878c2ecf20Sopenharmony_ci \ 888c2ecf20Sopenharmony_ci data[i++] += sinfo.tx_packets; \ 898c2ecf20Sopenharmony_ci data[i++] += sinfo.tx_bytes; \ 908c2ecf20Sopenharmony_ci data[i++] += sta->status_stats.filtered; \ 918c2ecf20Sopenharmony_ci data[i++] += sta->status_stats.retry_failed; \ 928c2ecf20Sopenharmony_ci data[i++] += sta->status_stats.retry_count; \ 938c2ecf20Sopenharmony_ci } while (0) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* For Managed stations, find the single station based on BSSID 968c2ecf20Sopenharmony_ci * and use that. For interface types, iterate through all available 978c2ecf20Sopenharmony_ci * stations and add stats for any station that is assigned to this 988c2ecf20Sopenharmony_ci * network device. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci mutex_lock(&local->sta_mtx); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (sdata->vif.type == NL80211_IFTYPE_STATION) { 1048c2ecf20Sopenharmony_ci sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!(sta && !WARN_ON(sta->sdata->dev != dev))) 1078c2ecf20Sopenharmony_ci goto do_survey; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci memset(&sinfo, 0, sizeof(sinfo)); 1108c2ecf20Sopenharmony_ci sta_set_sinfo(sta, &sinfo, false); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci i = 0; 1138c2ecf20Sopenharmony_ci ADD_STA_STATS(sta); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci data[i++] = sta->sta_state; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) 1198c2ecf20Sopenharmony_ci data[i] = 100000ULL * 1208c2ecf20Sopenharmony_ci cfg80211_calculate_bitrate(&sinfo.txrate); 1218c2ecf20Sopenharmony_ci i++; 1228c2ecf20Sopenharmony_ci if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) 1238c2ecf20Sopenharmony_ci data[i] = 100000ULL * 1248c2ecf20Sopenharmony_ci cfg80211_calculate_bitrate(&sinfo.rxrate); 1258c2ecf20Sopenharmony_ci i++; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG)) 1288c2ecf20Sopenharmony_ci data[i] = (u8)sinfo.signal_avg; 1298c2ecf20Sopenharmony_ci i++; 1308c2ecf20Sopenharmony_ci } else { 1318c2ecf20Sopenharmony_ci list_for_each_entry(sta, &local->sta_list, list) { 1328c2ecf20Sopenharmony_ci /* Make sure this station belongs to the proper dev */ 1338c2ecf20Sopenharmony_ci if (sta->sdata->dev != dev) 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci memset(&sinfo, 0, sizeof(sinfo)); 1378c2ecf20Sopenharmony_ci sta_set_sinfo(sta, &sinfo, false); 1388c2ecf20Sopenharmony_ci i = 0; 1398c2ecf20Sopenharmony_ci ADD_STA_STATS(sta); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cido_survey: 1448c2ecf20Sopenharmony_ci i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; 1458c2ecf20Sopenharmony_ci /* Get survey stats for current channel */ 1468c2ecf20Sopenharmony_ci survey.filled = 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci rcu_read_lock(); 1498c2ecf20Sopenharmony_ci chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 1508c2ecf20Sopenharmony_ci if (chanctx_conf) 1518c2ecf20Sopenharmony_ci channel = chanctx_conf->def.chan; 1528c2ecf20Sopenharmony_ci else 1538c2ecf20Sopenharmony_ci channel = NULL; 1548c2ecf20Sopenharmony_ci rcu_read_unlock(); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (channel) { 1578c2ecf20Sopenharmony_ci q = 0; 1588c2ecf20Sopenharmony_ci do { 1598c2ecf20Sopenharmony_ci survey.filled = 0; 1608c2ecf20Sopenharmony_ci if (drv_get_survey(local, q, &survey) != 0) { 1618c2ecf20Sopenharmony_ci survey.filled = 0; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci q++; 1658c2ecf20Sopenharmony_ci } while (channel != survey.channel); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (survey.filled) 1698c2ecf20Sopenharmony_ci data[i++] = survey.channel->center_freq; 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci data[i++] = 0; 1728c2ecf20Sopenharmony_ci if (survey.filled & SURVEY_INFO_NOISE_DBM) 1738c2ecf20Sopenharmony_ci data[i++] = (u8)survey.noise; 1748c2ecf20Sopenharmony_ci else 1758c2ecf20Sopenharmony_ci data[i++] = -1LL; 1768c2ecf20Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME) 1778c2ecf20Sopenharmony_ci data[i++] = survey.time; 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci data[i++] = -1LL; 1808c2ecf20Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_BUSY) 1818c2ecf20Sopenharmony_ci data[i++] = survey.time_busy; 1828c2ecf20Sopenharmony_ci else 1838c2ecf20Sopenharmony_ci data[i++] = -1LL; 1848c2ecf20Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_EXT_BUSY) 1858c2ecf20Sopenharmony_ci data[i++] = survey.time_ext_busy; 1868c2ecf20Sopenharmony_ci else 1878c2ecf20Sopenharmony_ci data[i++] = -1LL; 1888c2ecf20Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_RX) 1898c2ecf20Sopenharmony_ci data[i++] = survey.time_rx; 1908c2ecf20Sopenharmony_ci else 1918c2ecf20Sopenharmony_ci data[i++] = -1LL; 1928c2ecf20Sopenharmony_ci if (survey.filled & SURVEY_INFO_TIME_TX) 1938c2ecf20Sopenharmony_ci data[i++] = survey.time_tx; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci data[i++] = -1LL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mutex_unlock(&local->sta_mtx); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (WARN_ON(i != STA_STATS_LEN)) 2008c2ecf20Sopenharmony_ci return; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 2088c2ecf20Sopenharmony_ci int sz_sta_stats = 0; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (sset == ETH_SS_STATS) { 2118c2ecf20Sopenharmony_ci sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); 2128c2ecf20Sopenharmony_ci memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int ieee80211_get_regs_len(struct net_device *dev) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void ieee80211_get_regs(struct net_device *dev, 2238c2ecf20Sopenharmony_ci struct ethtool_regs *regs, 2248c2ecf20Sopenharmony_ci void *data) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci regs->version = wdev->wiphy->hw_version; 2298c2ecf20Sopenharmony_ci regs->len = 0; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciconst struct ethtool_ops ieee80211_ethtool_ops = { 2338c2ecf20Sopenharmony_ci .get_drvinfo = cfg80211_get_drvinfo, 2348c2ecf20Sopenharmony_ci .get_regs_len = ieee80211_get_regs_len, 2358c2ecf20Sopenharmony_ci .get_regs = ieee80211_get_regs, 2368c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 2378c2ecf20Sopenharmony_ci .get_ringparam = ieee80211_get_ringparam, 2388c2ecf20Sopenharmony_ci .set_ringparam = ieee80211_set_ringparam, 2398c2ecf20Sopenharmony_ci .get_strings = ieee80211_get_strings, 2408c2ecf20Sopenharmony_ci .get_ethtool_stats = ieee80211_get_stats, 2418c2ecf20Sopenharmony_ci .get_sset_count = ieee80211_get_sset_count, 2428c2ecf20Sopenharmony_ci}; 243