1 | /* |
2 | * Copyright (c) 2013 Qualcomm Atheros, Inc. |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include "ath9k.h" |
18 | |
19 | /*************/ |
20 | /* node_aggr */ |
21 | /*************/ |
22 | |
23 | static ssize_t read_file_node_aggr(struct file *file, char __user *user_buf, |
24 | size_t count, loff_t *ppos) |
25 | { |
26 | struct ath_node *an = file->private_data; |
27 | struct ath_softc *sc = an->sc; |
28 | struct ath_atx_tid *tid; |
29 | struct ath_txq *txq; |
30 | u32 len = 0, size = 4096; |
31 | char *buf; |
32 | size_t retval; |
33 | int tidno; |
34 | |
35 | buf = kzalloc(size, GFP_KERNEL); |
36 | if (buf == NULL) |
37 | return -ENOMEM; |
38 | |
39 | if (!an->sta->deflink.ht_cap.ht_supported) { |
40 | len = scnprintf(buf, size, fmt: "%s\n" , |
41 | "HT not supported" ); |
42 | goto exit; |
43 | } |
44 | |
45 | len = scnprintf(buf, size, fmt: "Max-AMPDU: %d\n" , |
46 | an->maxampdu); |
47 | len += scnprintf(buf: buf + len, size: size - len, fmt: "MPDU Density: %d\n\n" , |
48 | an->mpdudensity); |
49 | |
50 | len += scnprintf(buf: buf + len, size: size - len, |
51 | fmt: "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n" , |
52 | "TID" , "SEQ_START" , "SEQ_NEXT" , "BAW_SIZE" , |
53 | "BAW_HEAD" , "BAW_TAIL" , "BAR_IDX" , "SCHED" , "PAUSED" ); |
54 | |
55 | for (tidno = 0; tidno < IEEE80211_NUM_TIDS; tidno++) { |
56 | tid = ath_node_to_tid(an, tidno); |
57 | txq = tid->txq; |
58 | ath_txq_lock(sc, txq); |
59 | if (tid->active) { |
60 | len += scnprintf(buf: buf + len, size: size - len, |
61 | fmt: "%3d%11d%10d%10d%10d%10d%9d%6d\n" , |
62 | tid->tidno, |
63 | tid->seq_start, |
64 | tid->seq_next, |
65 | tid->baw_size, |
66 | tid->baw_head, |
67 | tid->baw_tail, |
68 | tid->bar_index, |
69 | !list_empty(head: &tid->list)); |
70 | } |
71 | ath_txq_unlock(sc, txq); |
72 | } |
73 | exit: |
74 | retval = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
75 | kfree(objp: buf); |
76 | |
77 | return retval; |
78 | } |
79 | |
80 | static const struct file_operations fops_node_aggr = { |
81 | .read = read_file_node_aggr, |
82 | .open = simple_open, |
83 | .owner = THIS_MODULE, |
84 | .llseek = default_llseek, |
85 | }; |
86 | |
87 | /*************/ |
88 | /* node_recv */ |
89 | /*************/ |
90 | |
91 | void ath_debug_rate_stats(struct ath_softc *sc, |
92 | struct ath_rx_status *rs, |
93 | struct sk_buff *skb) |
94 | { |
95 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
96 | struct ath_hw *ah = sc->sc_ah; |
97 | struct ieee80211_rx_status *rxs; |
98 | struct ath_rx_rate_stats *rstats; |
99 | struct ieee80211_sta *sta; |
100 | struct ath_node *an; |
101 | |
102 | if (!ieee80211_is_data(fc: hdr->frame_control)) |
103 | return; |
104 | |
105 | rcu_read_lock(); |
106 | |
107 | sta = ieee80211_find_sta_by_ifaddr(hw: sc->hw, addr: hdr->addr2, NULL); |
108 | if (!sta) |
109 | goto exit; |
110 | |
111 | an = (struct ath_node *) sta->drv_priv; |
112 | rstats = &an->rx_rate_stats; |
113 | rxs = IEEE80211_SKB_RXCB(skb); |
114 | |
115 | if (IS_HT_RATE(rs->rs_rate)) { |
116 | if (rxs->rate_idx >= ARRAY_SIZE(rstats->ht_stats)) |
117 | goto exit; |
118 | |
119 | if (rxs->bw == RATE_INFO_BW_40) |
120 | rstats->ht_stats[rxs->rate_idx].ht40_cnt++; |
121 | else |
122 | rstats->ht_stats[rxs->rate_idx].ht20_cnt++; |
123 | |
124 | if (rxs->enc_flags & RX_ENC_FLAG_SHORT_GI) |
125 | rstats->ht_stats[rxs->rate_idx].sgi_cnt++; |
126 | else |
127 | rstats->ht_stats[rxs->rate_idx].lgi_cnt++; |
128 | |
129 | goto exit; |
130 | } |
131 | |
132 | if (IS_CCK_RATE(rs->rs_rate)) { |
133 | if (rxs->enc_flags & RX_ENC_FLAG_SHORTPRE) |
134 | rstats->cck_stats[rxs->rate_idx].cck_sp_cnt++; |
135 | else |
136 | rstats->cck_stats[rxs->rate_idx].cck_lp_cnt++; |
137 | |
138 | goto exit; |
139 | } |
140 | |
141 | if (IS_OFDM_RATE(rs->rs_rate)) { |
142 | if (ah->curchan->chan->band == NL80211_BAND_2GHZ) |
143 | rstats->ofdm_stats[rxs->rate_idx - 4].ofdm_cnt++; |
144 | else |
145 | rstats->ofdm_stats[rxs->rate_idx].ofdm_cnt++; |
146 | } |
147 | exit: |
148 | rcu_read_unlock(); |
149 | } |
150 | |
151 | #define PRINT_CCK_RATE(str, i, sp) \ |
152 | do { \ |
153 | len += scnprintf(buf + len, size - len, \ |
154 | "%11s : %10u\n", \ |
155 | str, \ |
156 | (sp) ? rstats->cck_stats[i].cck_sp_cnt : \ |
157 | rstats->cck_stats[i].cck_lp_cnt); \ |
158 | } while (0) |
159 | |
160 | #define PRINT_OFDM_RATE(str, i) \ |
161 | do { \ |
162 | len += scnprintf(buf + len, size - len, \ |
163 | "%11s : %10u\n", \ |
164 | str, \ |
165 | rstats->ofdm_stats[i].ofdm_cnt); \ |
166 | } while (0) |
167 | |
168 | static ssize_t read_file_node_recv(struct file *file, char __user *user_buf, |
169 | size_t count, loff_t *ppos) |
170 | { |
171 | struct ath_node *an = file->private_data; |
172 | struct ath_softc *sc = an->sc; |
173 | struct ath_hw *ah = sc->sc_ah; |
174 | struct ath_rx_rate_stats *rstats; |
175 | struct ieee80211_sta *sta = an->sta; |
176 | enum nl80211_band band; |
177 | u32 len = 0, size = 4096; |
178 | char *buf; |
179 | size_t retval; |
180 | int i; |
181 | |
182 | buf = kzalloc(size, GFP_KERNEL); |
183 | if (buf == NULL) |
184 | return -ENOMEM; |
185 | |
186 | band = ah->curchan->chan->band; |
187 | rstats = &an->rx_rate_stats; |
188 | |
189 | if (!sta->deflink.ht_cap.ht_supported) |
190 | goto legacy; |
191 | |
192 | len += scnprintf(buf: buf + len, size: size - len, |
193 | fmt: "%24s%10s%10s%10s\n" , |
194 | "HT20" , "HT40" , "SGI" , "LGI" ); |
195 | |
196 | for (i = 0; i < 24; i++) { |
197 | len += scnprintf(buf: buf + len, size: size - len, |
198 | fmt: "%8s%3u : %10u%10u%10u%10u\n" , |
199 | "MCS" , i, |
200 | rstats->ht_stats[i].ht20_cnt, |
201 | rstats->ht_stats[i].ht40_cnt, |
202 | rstats->ht_stats[i].sgi_cnt, |
203 | rstats->ht_stats[i].lgi_cnt); |
204 | } |
205 | |
206 | len += scnprintf(buf: buf + len, size: size - len, fmt: "\n" ); |
207 | |
208 | legacy: |
209 | if (band == NL80211_BAND_2GHZ) { |
210 | PRINT_CCK_RATE("CCK-1M/LP" , 0, false); |
211 | PRINT_CCK_RATE("CCK-2M/LP" , 1, false); |
212 | PRINT_CCK_RATE("CCK-5.5M/LP" , 2, false); |
213 | PRINT_CCK_RATE("CCK-11M/LP" , 3, false); |
214 | |
215 | PRINT_CCK_RATE("CCK-2M/SP" , 1, true); |
216 | PRINT_CCK_RATE("CCK-5.5M/SP" , 2, true); |
217 | PRINT_CCK_RATE("CCK-11M/SP" , 3, true); |
218 | } |
219 | |
220 | PRINT_OFDM_RATE("OFDM-6M" , 0); |
221 | PRINT_OFDM_RATE("OFDM-9M" , 1); |
222 | PRINT_OFDM_RATE("OFDM-12M" , 2); |
223 | PRINT_OFDM_RATE("OFDM-18M" , 3); |
224 | PRINT_OFDM_RATE("OFDM-24M" , 4); |
225 | PRINT_OFDM_RATE("OFDM-36M" , 5); |
226 | PRINT_OFDM_RATE("OFDM-48M" , 6); |
227 | PRINT_OFDM_RATE("OFDM-54M" , 7); |
228 | |
229 | retval = simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
230 | kfree(objp: buf); |
231 | |
232 | return retval; |
233 | } |
234 | |
235 | #undef PRINT_OFDM_RATE |
236 | #undef PRINT_CCK_RATE |
237 | |
238 | static const struct file_operations fops_node_recv = { |
239 | .read = read_file_node_recv, |
240 | .open = simple_open, |
241 | .owner = THIS_MODULE, |
242 | .llseek = default_llseek, |
243 | }; |
244 | |
245 | void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, |
246 | struct ieee80211_vif *vif, |
247 | struct ieee80211_sta *sta, |
248 | struct dentry *dir) |
249 | { |
250 | struct ath_node *an = (struct ath_node *)sta->drv_priv; |
251 | |
252 | debugfs_create_file(name: "node_aggr" , mode: 0444, parent: dir, data: an, fops: &fops_node_aggr); |
253 | debugfs_create_file(name: "node_recv" , mode: 0444, parent: dir, data: an, fops: &fops_node_recv); |
254 | } |
255 | |