diff options
Diffstat (limited to 'net/mac80211/ethtool.c')
-rw-r--r-- | net/mac80211/ethtool.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c new file mode 100644 index 000000000000..ebfc8091557b --- /dev/null +++ b/net/mac80211/ethtool.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * mac80211 ethtool hooks for cfg80211 | ||
3 | * | ||
4 | * Copied from cfg.c - originally | ||
5 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> | ||
6 | * Copyright 2014 Intel Corporation (Author: Johannes Berg) | ||
7 | * | ||
8 | * This file is GPLv2 as found in COPYING. | ||
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 | |||
16 | static int ieee80211_set_ringparam(struct net_device *dev, | ||
17 | struct ethtool_ringparam *rp) | ||
18 | { | ||
19 | struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); | ||
20 | |||
21 | if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) | ||
22 | return -EINVAL; | ||
23 | |||
24 | return drv_set_ringparam(local, rp->tx_pending, rp->rx_pending); | ||
25 | } | ||
26 | |||
27 | static void ieee80211_get_ringparam(struct net_device *dev, | ||
28 | struct ethtool_ringparam *rp) | ||
29 | { | ||
30 | struct ieee80211_local *local = wiphy_priv(dev->ieee80211_ptr->wiphy); | ||
31 | |||
32 | memset(rp, 0, sizeof(*rp)); | ||
33 | |||
34 | drv_get_ringparam(local, &rp->tx_pending, &rp->tx_max_pending, | ||
35 | &rp->rx_pending, &rp->rx_max_pending); | ||
36 | } | ||
37 | |||
38 | static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = { | ||
39 | "rx_packets", "rx_bytes", | ||
40 | "rx_duplicates", "rx_fragments", "rx_dropped", | ||
41 | "tx_packets", "tx_bytes", "tx_fragments", | ||
42 | "tx_filtered", "tx_retry_failed", "tx_retries", | ||
43 | "beacon_loss", "sta_state", "txrate", "rxrate", "signal", | ||
44 | "channel", "noise", "ch_time", "ch_time_busy", | ||
45 | "ch_time_ext_busy", "ch_time_rx", "ch_time_tx" | ||
46 | }; | ||
47 | #define STA_STATS_LEN ARRAY_SIZE(ieee80211_gstrings_sta_stats) | ||
48 | |||
49 | static int ieee80211_get_sset_count(struct net_device *dev, int sset) | ||
50 | { | ||
51 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
52 | int rv = 0; | ||
53 | |||
54 | if (sset == ETH_SS_STATS) | ||
55 | rv += STA_STATS_LEN; | ||
56 | |||
57 | rv += drv_get_et_sset_count(sdata, sset); | ||
58 | |||
59 | if (rv == 0) | ||
60 | return -EOPNOTSUPP; | ||
61 | return rv; | ||
62 | } | ||
63 | |||
64 | static void ieee80211_get_stats(struct net_device *dev, | ||
65 | struct ethtool_stats *stats, | ||
66 | u64 *data) | ||
67 | { | ||
68 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
69 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
70 | struct ieee80211_channel *channel; | ||
71 | struct sta_info *sta; | ||
72 | struct ieee80211_local *local = sdata->local; | ||
73 | struct station_info sinfo; | ||
74 | struct survey_info survey; | ||
75 | int i, q; | ||
76 | #define STA_STATS_SURVEY_LEN 7 | ||
77 | |||
78 | memset(data, 0, sizeof(u64) * STA_STATS_LEN); | ||
79 | |||
80 | #define ADD_STA_STATS(sta) \ | ||
81 | do { \ | ||
82 | data[i++] += sta->rx_packets; \ | ||
83 | data[i++] += sta->rx_bytes; \ | ||
84 | data[i++] += sta->num_duplicates; \ | ||
85 | data[i++] += sta->rx_fragments; \ | ||
86 | data[i++] += sta->rx_dropped; \ | ||
87 | \ | ||
88 | data[i++] += sinfo.tx_packets; \ | ||
89 | data[i++] += sinfo.tx_bytes; \ | ||
90 | data[i++] += sta->tx_fragments; \ | ||
91 | data[i++] += sta->tx_filtered_count; \ | ||
92 | data[i++] += sta->tx_retry_failed; \ | ||
93 | data[i++] += sta->tx_retry_count; \ | ||
94 | data[i++] += sta->beacon_loss_count; \ | ||
95 | } while (0) | ||
96 | |||
97 | /* For Managed stations, find the single station based on BSSID | ||
98 | * and use that. For interface types, iterate through all available | ||
99 | * stations and add stats for any station that is assigned to this | ||
100 | * network device. | ||
101 | */ | ||
102 | |||
103 | mutex_lock(&local->sta_mtx); | ||
104 | |||
105 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
106 | sta = sta_info_get_bss(sdata, sdata->u.mgd.bssid); | ||
107 | |||
108 | if (!(sta && !WARN_ON(sta->sdata->dev != dev))) | ||
109 | goto do_survey; | ||
110 | |||
111 | sinfo.filled = 0; | ||
112 | sta_set_sinfo(sta, &sinfo); | ||
113 | |||
114 | i = 0; | ||
115 | ADD_STA_STATS(sta); | ||
116 | |||
117 | data[i++] = sta->sta_state; | ||
118 | |||
119 | |||
120 | if (sinfo.filled & STATION_INFO_TX_BITRATE) | ||
121 | data[i] = 100000 * | ||
122 | cfg80211_calculate_bitrate(&sinfo.txrate); | ||
123 | i++; | ||
124 | if (sinfo.filled & STATION_INFO_RX_BITRATE) | ||
125 | data[i] = 100000 * | ||
126 | cfg80211_calculate_bitrate(&sinfo.rxrate); | ||
127 | i++; | ||
128 | |||
129 | if (sinfo.filled & STATION_INFO_SIGNAL_AVG) | ||
130 | data[i] = (u8)sinfo.signal_avg; | ||
131 | i++; | ||
132 | } else { | ||
133 | list_for_each_entry(sta, &local->sta_list, list) { | ||
134 | /* Make sure this station belongs to the proper dev */ | ||
135 | if (sta->sdata->dev != dev) | ||
136 | continue; | ||
137 | |||
138 | sinfo.filled = 0; | ||
139 | sta_set_sinfo(sta, &sinfo); | ||
140 | i = 0; | ||
141 | ADD_STA_STATS(sta); | ||
142 | } | ||
143 | } | ||
144 | |||
145 | do_survey: | ||
146 | i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; | ||
147 | /* Get survey stats for current channel */ | ||
148 | survey.filled = 0; | ||
149 | |||
150 | rcu_read_lock(); | ||
151 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
152 | if (chanctx_conf) | ||
153 | channel = chanctx_conf->def.chan; | ||
154 | else | ||
155 | channel = NULL; | ||
156 | rcu_read_unlock(); | ||
157 | |||
158 | if (channel) { | ||
159 | q = 0; | ||
160 | do { | ||
161 | survey.filled = 0; | ||
162 | if (drv_get_survey(local, q, &survey) != 0) { | ||
163 | survey.filled = 0; | ||
164 | break; | ||
165 | } | ||
166 | q++; | ||
167 | } while (channel != survey.channel); | ||
168 | } | ||
169 | |||
170 | if (survey.filled) | ||
171 | data[i++] = survey.channel->center_freq; | ||
172 | else | ||
173 | data[i++] = 0; | ||
174 | if (survey.filled & SURVEY_INFO_NOISE_DBM) | ||
175 | data[i++] = (u8)survey.noise; | ||
176 | else | ||
177 | data[i++] = -1LL; | ||
178 | if (survey.filled & SURVEY_INFO_CHANNEL_TIME) | ||
179 | data[i++] = survey.channel_time; | ||
180 | else | ||
181 | data[i++] = -1LL; | ||
182 | if (survey.filled & SURVEY_INFO_CHANNEL_TIME_BUSY) | ||
183 | data[i++] = survey.channel_time_busy; | ||
184 | else | ||
185 | data[i++] = -1LL; | ||
186 | if (survey.filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) | ||
187 | data[i++] = survey.channel_time_ext_busy; | ||
188 | else | ||
189 | data[i++] = -1LL; | ||
190 | if (survey.filled & SURVEY_INFO_CHANNEL_TIME_RX) | ||
191 | data[i++] = survey.channel_time_rx; | ||
192 | else | ||
193 | data[i++] = -1LL; | ||
194 | if (survey.filled & SURVEY_INFO_CHANNEL_TIME_TX) | ||
195 | data[i++] = survey.channel_time_tx; | ||
196 | else | ||
197 | data[i++] = -1LL; | ||
198 | |||
199 | mutex_unlock(&local->sta_mtx); | ||
200 | |||
201 | if (WARN_ON(i != STA_STATS_LEN)) | ||
202 | return; | ||
203 | |||
204 | drv_get_et_stats(sdata, stats, &(data[STA_STATS_LEN])); | ||
205 | } | ||
206 | |||
207 | static void ieee80211_get_strings(struct net_device *dev, u32 sset, u8 *data) | ||
208 | { | ||
209 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
210 | int sz_sta_stats = 0; | ||
211 | |||
212 | if (sset == ETH_SS_STATS) { | ||
213 | sz_sta_stats = sizeof(ieee80211_gstrings_sta_stats); | ||
214 | memcpy(data, ieee80211_gstrings_sta_stats, sz_sta_stats); | ||
215 | } | ||
216 | drv_get_et_strings(sdata, sset, &(data[sz_sta_stats])); | ||
217 | } | ||
218 | |||
219 | static int ieee80211_get_regs_len(struct net_device *dev) | ||
220 | { | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static void ieee80211_get_regs(struct net_device *dev, | ||
225 | struct ethtool_regs *regs, | ||
226 | void *data) | ||
227 | { | ||
228 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
229 | |||
230 | regs->version = wdev->wiphy->hw_version; | ||
231 | regs->len = 0; | ||
232 | } | ||
233 | |||
234 | const struct ethtool_ops ieee80211_ethtool_ops = { | ||
235 | .get_drvinfo = cfg80211_get_drvinfo, | ||
236 | .get_regs_len = ieee80211_get_regs_len, | ||
237 | .get_regs = ieee80211_get_regs, | ||
238 | .get_link = ethtool_op_get_link, | ||
239 | .get_ringparam = ieee80211_get_ringparam, | ||
240 | .set_ringparam = ieee80211_set_ringparam, | ||
241 | .get_strings = ieee80211_get_strings, | ||
242 | .get_ethtool_stats = ieee80211_get_stats, | ||
243 | .get_sset_count = ieee80211_get_sset_count, | ||
244 | }; | ||