ethtool.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * mac80211 ethtool hooks for cfg80211
  4. *
  5. * Copied from cfg.c - originally
  6. * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  7. * Copyright 2014 Intel Corporation (Author: Johannes Berg)
  8. * Copyright (C) 2018, 2022-2023 Intel Corporation
  9. */
  10. #include <linux/types.h>
  11. #include <net/cfg80211.h>
  12. #include "ieee80211_i.h"
  13. #include "sta_info.h"
  14. #include "driver-ops.h"
  15. static int ieee80211_set_ringparam(struct net_device *dev,
  16. struct ethtool_ringparam *rp,
  17. struct kernel_ethtool_ringparam *kernel_rp,
  18. struct netlink_ext_ack *extack)
  19. {
  20. struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
  21. int ret;
  22. if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0)
  23. return -EINVAL;
  24. wiphy_lock(local->hw.wiphy);
  25. ret = drv_set_ringparam(local, rp->tx_pending, rp->rx_pending);
  26. wiphy_unlock(local->hw.wiphy);
  27. return ret;
  28. }
  29. static void ieee80211_get_ringparam(struct net_device *dev,
  30. struct ethtool_ringparam *rp,
  31. struct kernel_ethtool_ringparam *kernel_rp,
  32. struct netlink_ext_ack *extack)
  33. {
  34. struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy);
  35. memset(rp, 0, sizeof(*rp));
  36. wiphy_lock(local->hw.wiphy);
  37. drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending,
  38. &rp->rx_pending, &rp->rx_max_pending);
  39. wiphy_unlock(local->hw.wiphy);
  40. }
  41. static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
  42. "rx_packets", "rx_bytes",
  43. "rx_duplicates", "rx_fragments", "rx_dropped",
  44. "tx_packets", "tx_bytes",
  45. "tx_filtered", "tx_retry_failed", "tx_retries",
  46. "sta_state", "txrate", "rxrate", "signal",
  47. "channel", "noise", "ch_time", "ch_time_busy",
  48. "ch_time_ext_busy", "ch_time_rx", "ch_time_tx"
  49. };
  50. #define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats)
  51. static int ieee80211_get_sset_count(struct net_device *dev, int sset)
  52. {
  53. struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  54. int rv = 0;
  55. if (sset == ETH_SS_STATS)
  56. rv += STA_STATS_LEN;
  57. rv += drv_get_et_sset_count(sdata, sset);
  58. if (rv == 0)
  59. return -EOPNOTSUPP;
  60. return rv;
  61. }
  62. static void ieee80211_get_stats(struct net_device *dev,
  63. struct ethtool_stats *stats,
  64. u64 *data)
  65. {
  66. struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  67. struct ieee80211_chanctx_conf *chanctx_conf;
  68. struct ieee80211_channel *channel;
  69. struct sta_info *sta;
  70. struct ieee80211_local *local = sdata->local;
  71. struct station_info sinfo;
  72. struct survey_info survey;
  73. int i, q;
  74. #define STA_STATS_SURVEY_LEN 7
  75. memset(data, 0, sizeof(u64) * STA_STATS_LEN);
  76. #define ADD_STA_STATS(sta) \
  77. do { \
  78. data[i++] += sinfo.rx_packets; \
  79. data[i++] += sinfo.rx_bytes; \
  80. data[i++] += (sta)->rx_stats.num_duplicates; \
  81. data[i++] += (sta)->rx_stats.fragments; \
  82. data[i++] += sinfo.rx_dropped_misc; \
  83. \
  84. data[i++] += sinfo.tx_packets; \
  85. data[i++] += sinfo.tx_bytes; \
  86. data[i++] += (sta)->status_stats.filtered; \
  87. data[i++] += sinfo.tx_failed; \
  88. data[i++] += sinfo.tx_retries; \
  89. } while (0)
  90. /* For Managed stations, find the single station based on BSSID
  91. * and use that. For interface types, iterate through all available
  92. * stations and add stats for any station that is assigned to this
  93. * network device.
  94. */
  95. wiphy_lock(local->hw.wiphy);
  96. if (sdata->vif.type == NL80211_IFTYPE_STATION) {
  97. sta = sta_info_get_bss(sdata, sdata->deflink.u.mgd.bssid);
  98. if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
  99. goto do_survey;
  100. memset(&sinfo, 0, sizeof(sinfo));
  101. sta_set_sinfo(sta, &sinfo, false);
  102. i = 0;
  103. ADD_STA_STATS(&sta->deflink);
  104. data[i++] = sta->sta_state;
  105. if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE))
  106. data[i] = 100000ULL *
  107. cfg80211_calculate_bitrate(&sinfo.txrate);
  108. i++;
  109. if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE))
  110. data[i] = 100000ULL *
  111. cfg80211_calculate_bitrate(&sinfo.rxrate);
  112. i++;
  113. if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG))
  114. data[i] = (u8)sinfo.signal_avg;
  115. i++;
  116. } else {
  117. list_for_each_entry(sta, &local->sta_list, list) {
  118. /* Make sure this station belongs to the proper dev */
  119. if (sta->sdata->dev != dev)
  120. continue;
  121. memset(&sinfo, 0, sizeof(sinfo));
  122. sta_set_sinfo(sta, &sinfo, false);
  123. i = 0;
  124. ADD_STA_STATS(&sta->deflink);
  125. }
  126. }
  127. do_survey:
  128. i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
  129. /* Get survey stats for current channel */
  130. survey.filled = 0;
  131. rcu_read_lock();
  132. chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
  133. if (chanctx_conf)
  134. channel = chanctx_conf->def.chan;
  135. else
  136. channel = NULL;
  137. rcu_read_unlock();
  138. if (channel) {
  139. q = 0;
  140. do {
  141. survey.filled = 0;
  142. if (drv_get_survey(local, q, &survey) != 0) {
  143. survey.filled = 0;
  144. break;
  145. }
  146. q++;
  147. } while (channel != survey.channel);
  148. }
  149. if (survey.filled)
  150. data[i++] = survey.channel->center_freq;
  151. else
  152. data[i++] = 0;
  153. if (survey.filled & SURVEY_INFO_NOISE_DBM)
  154. data[i++] = (u8)survey.noise;
  155. else
  156. data[i++] = -1LL;
  157. if (survey.filled & SURVEY_INFO_TIME)
  158. data[i++] = survey.time;
  159. else
  160. data[i++] = -1LL;
  161. if (survey.filled & SURVEY_INFO_TIME_BUSY)
  162. data[i++] = survey.time_busy;
  163. else
  164. data[i++] = -1LL;
  165. if (survey.filled & SURVEY_INFO_TIME_EXT_BUSY)
  166. data[i++] = survey.time_ext_busy;
  167. else
  168. data[i++] = -1LL;
  169. if (survey.filled & SURVEY_INFO_TIME_RX)
  170. data[i++] = survey.time_rx;
  171. else
  172. data[i++] = -1LL;
  173. if (survey.filled & SURVEY_INFO_TIME_TX)
  174. data[i++] = survey.time_tx;
  175. else
  176. data[i++] = -1LL;
  177. if (WARN_ON(i != STA_STATS_LEN)) {
  178. wiphy_unlock(local->hw.wiphy);
  179. return;
  180. }
  181. drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN]));
  182. wiphy_unlock(local->hw.wiphy);
  183. }
  184. static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
  185. {
  186. struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
  187. int sz_sta_stats = 0;
  188. if (sset == ETH_SS_STATS) {
  189. sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats);
  190. memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats);
  191. }
  192. drv_get_et_strings(sdata, sset, &(data[sz_sta_stats]));
  193. }
  194. static int ieee80211_get_regs_len(struct net_device *dev)
  195. {
  196. return 0;
  197. }
  198. static void ieee80211_get_regs(struct net_device *dev,
  199. struct ethtool_regs *regs,
  200. void *data)
  201. {
  202. struct wireless_dev *wdev = dev->ieee80211_ptr;
  203. regs->version = wdev->wiphy->hw_version;
  204. regs->len = 0;
  205. }
  206. const struct ethtool_ops ieee80211_ethtool_ops = {
  207. .get_drvinfo = cfg80211_get_drvinfo,
  208. .get_regs_len = ieee80211_get_regs_len,
  209. .get_regs = ieee80211_get_regs,
  210. .get_link = ethtool_op_get_link,
  211. .get_ringparam = ieee80211_get_ringparam,
  212. .set_ringparam = ieee80211_set_ringparam,
  213. .get_strings = ieee80211_get_strings,
  214. .get_ethtool_stats = ieee80211_get_stats,
  215. .get_sset_count = ieee80211_get_sset_count,
  216. };