1 | /* |
2 | * Copyright (c) 2007-2008 Bruno Randolf <bruno@thinktube.com> |
3 | * |
4 | * This file is free software: you may copy, redistribute and/or modify it |
5 | * under the terms of the GNU General Public License as published by the |
6 | * Free Software Foundation, either version 2 of the License, or (at your |
7 | * option) any later version. |
8 | * |
9 | * This file is distributed in the hope that it will be useful, but |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * |
18 | * This file incorporates work covered by the following copyright and |
19 | * permission notice: |
20 | * |
21 | * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting |
22 | * Copyright (c) 2004-2005 Atheros Communications, Inc. |
23 | * Copyright (c) 2006 Devicescape Software, Inc. |
24 | * Copyright (c) 2007 Jiri Slaby <jirislaby@gmail.com> |
25 | * Copyright (c) 2007 Luis R. Rodriguez <mcgrof@winlab.rutgers.edu> |
26 | * |
27 | * All rights reserved. |
28 | * |
29 | * Redistribution and use in source and binary forms, with or without |
30 | * modification, are permitted provided that the following conditions |
31 | * are met: |
32 | * 1. Redistributions of source code must retain the above copyright |
33 | * notice, this list of conditions and the following disclaimer, |
34 | * without modification. |
35 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
36 | * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any |
37 | * redistribution must be conditioned upon including a substantially |
38 | * similar Disclaimer requirement for further binary redistribution. |
39 | * 3. Neither the names of the above-listed copyright holders nor the names |
40 | * of any contributors may be used to endorse or promote products derived |
41 | * from this software without specific prior written permission. |
42 | * |
43 | * Alternatively, this software may be distributed under the terms of the |
44 | * GNU General Public License ("GPL") version 2 as published by the Free |
45 | * Software Foundation. |
46 | * |
47 | * NO WARRANTY |
48 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
49 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
50 | * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY |
51 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
52 | * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, |
53 | * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
54 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
55 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
56 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
57 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
58 | * THE POSSIBILITY OF SUCH DAMAGES. |
59 | */ |
60 | |
61 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
62 | |
63 | #include <linux/export.h> |
64 | #include <linux/moduleparam.h> |
65 | #include <linux/vmalloc.h> |
66 | |
67 | #include <linux/seq_file.h> |
68 | #include <linux/list.h> |
69 | #include "debug.h" |
70 | #include "ath5k.h" |
71 | #include "reg.h" |
72 | #include "base.h" |
73 | |
74 | static unsigned int ath5k_debug; |
75 | module_param_named(debug, ath5k_debug, uint, 0); |
76 | |
77 | |
78 | /* debugfs: registers */ |
79 | |
80 | struct reg { |
81 | const char *name; |
82 | int addr; |
83 | }; |
84 | |
85 | #define REG_STRUCT_INIT(r) { #r, r } |
86 | |
87 | /* just a few random registers, might want to add more */ |
88 | static const struct reg regs[] = { |
89 | REG_STRUCT_INIT(AR5K_CR), |
90 | REG_STRUCT_INIT(AR5K_RXDP), |
91 | REG_STRUCT_INIT(AR5K_CFG), |
92 | REG_STRUCT_INIT(AR5K_IER), |
93 | REG_STRUCT_INIT(AR5K_BCR), |
94 | REG_STRUCT_INIT(AR5K_RTSD0), |
95 | REG_STRUCT_INIT(AR5K_RTSD1), |
96 | REG_STRUCT_INIT(AR5K_TXCFG), |
97 | REG_STRUCT_INIT(AR5K_RXCFG), |
98 | REG_STRUCT_INIT(AR5K_RXJLA), |
99 | REG_STRUCT_INIT(AR5K_MIBC), |
100 | REG_STRUCT_INIT(AR5K_TOPS), |
101 | REG_STRUCT_INIT(AR5K_RXNOFRM), |
102 | REG_STRUCT_INIT(AR5K_TXNOFRM), |
103 | REG_STRUCT_INIT(AR5K_RPGTO), |
104 | REG_STRUCT_INIT(AR5K_RFCNT), |
105 | REG_STRUCT_INIT(AR5K_MISC), |
106 | REG_STRUCT_INIT(AR5K_QCUDCU_CLKGT), |
107 | REG_STRUCT_INIT(AR5K_ISR), |
108 | REG_STRUCT_INIT(AR5K_PISR), |
109 | REG_STRUCT_INIT(AR5K_SISR0), |
110 | REG_STRUCT_INIT(AR5K_SISR1), |
111 | REG_STRUCT_INIT(AR5K_SISR2), |
112 | REG_STRUCT_INIT(AR5K_SISR3), |
113 | REG_STRUCT_INIT(AR5K_SISR4), |
114 | REG_STRUCT_INIT(AR5K_IMR), |
115 | REG_STRUCT_INIT(AR5K_PIMR), |
116 | REG_STRUCT_INIT(AR5K_SIMR0), |
117 | REG_STRUCT_INIT(AR5K_SIMR1), |
118 | REG_STRUCT_INIT(AR5K_SIMR2), |
119 | REG_STRUCT_INIT(AR5K_SIMR3), |
120 | REG_STRUCT_INIT(AR5K_SIMR4), |
121 | REG_STRUCT_INIT(AR5K_DCM_ADDR), |
122 | REG_STRUCT_INIT(AR5K_DCCFG), |
123 | REG_STRUCT_INIT(AR5K_CCFG), |
124 | REG_STRUCT_INIT(AR5K_CPC0), |
125 | REG_STRUCT_INIT(AR5K_CPC1), |
126 | REG_STRUCT_INIT(AR5K_CPC2), |
127 | REG_STRUCT_INIT(AR5K_CPC3), |
128 | REG_STRUCT_INIT(AR5K_CPCOVF), |
129 | REG_STRUCT_INIT(AR5K_RESET_CTL), |
130 | REG_STRUCT_INIT(AR5K_SLEEP_CTL), |
131 | REG_STRUCT_INIT(AR5K_INTPEND), |
132 | REG_STRUCT_INIT(AR5K_SFR), |
133 | REG_STRUCT_INIT(AR5K_PCICFG), |
134 | REG_STRUCT_INIT(AR5K_GPIOCR), |
135 | REG_STRUCT_INIT(AR5K_GPIODO), |
136 | REG_STRUCT_INIT(AR5K_SREV), |
137 | }; |
138 | |
139 | static void *reg_start(struct seq_file *seq, loff_t *pos) |
140 | { |
141 | return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL; |
142 | } |
143 | |
144 | static void reg_stop(struct seq_file *seq, void *p) |
145 | { |
146 | /* nothing to do */ |
147 | } |
148 | |
149 | static void *reg_next(struct seq_file *seq, void *p, loff_t *pos) |
150 | { |
151 | ++*pos; |
152 | return *pos < ARRAY_SIZE(regs) ? (void *)®s[*pos] : NULL; |
153 | } |
154 | |
155 | static int reg_show(struct seq_file *seq, void *p) |
156 | { |
157 | struct ath5k_hw *ah = seq->private; |
158 | struct reg *r = p; |
159 | seq_printf(m: seq, fmt: "%-25s0x%08x\n" , r->name, |
160 | ath5k_hw_reg_read(ah, reg: r->addr)); |
161 | return 0; |
162 | } |
163 | |
164 | static const struct seq_operations registers_sops = { |
165 | .start = reg_start, |
166 | .next = reg_next, |
167 | .stop = reg_stop, |
168 | .show = reg_show |
169 | }; |
170 | |
171 | DEFINE_SEQ_ATTRIBUTE(registers); |
172 | |
173 | /* debugfs: beacons */ |
174 | |
175 | static ssize_t read_file_beacon(struct file *file, char __user *user_buf, |
176 | size_t count, loff_t *ppos) |
177 | { |
178 | struct ath5k_hw *ah = file->private_data; |
179 | char buf[500]; |
180 | unsigned int len = 0; |
181 | unsigned int v; |
182 | u64 tsf; |
183 | |
184 | v = ath5k_hw_reg_read(ah, AR5K_BEACON); |
185 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
186 | fmt: "%-24s0x%08x\tintval: %d\tTIM: 0x%x\n" , |
187 | "AR5K_BEACON" , v, v & AR5K_BEACON_PERIOD, |
188 | (v & AR5K_BEACON_TIM) >> AR5K_BEACON_TIM_S); |
189 | |
190 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "%-24s0x%08x\n" , |
191 | "AR5K_LAST_TSTP" , ath5k_hw_reg_read(ah, AR5K_LAST_TSTP)); |
192 | |
193 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "%-24s0x%08x\n\n" , |
194 | "AR5K_BEACON_CNT" , ath5k_hw_reg_read(ah, AR5K_BEACON_CNT)); |
195 | |
196 | v = ath5k_hw_reg_read(ah, AR5K_TIMER0); |
197 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "%-24s0x%08x\tTU: %08x\n" , |
198 | "AR5K_TIMER0 (TBTT)" , v, v); |
199 | |
200 | v = ath5k_hw_reg_read(ah, AR5K_TIMER1); |
201 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "%-24s0x%08x\tTU: %08x\n" , |
202 | "AR5K_TIMER1 (DMA)" , v, v >> 3); |
203 | |
204 | v = ath5k_hw_reg_read(ah, AR5K_TIMER2); |
205 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "%-24s0x%08x\tTU: %08x\n" , |
206 | "AR5K_TIMER2 (SWBA)" , v, v >> 3); |
207 | |
208 | v = ath5k_hw_reg_read(ah, AR5K_TIMER3); |
209 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "%-24s0x%08x\tTU: %08x\n" , |
210 | "AR5K_TIMER3 (ATIM)" , v, v); |
211 | |
212 | tsf = ath5k_hw_get_tsf64(ah); |
213 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
214 | fmt: "TSF\t\t0x%016llx\tTU: %08x\n" , |
215 | (unsigned long long)tsf, TSF_TO_TU(tsf)); |
216 | |
217 | if (len > sizeof(buf)) |
218 | len = sizeof(buf); |
219 | |
220 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
221 | } |
222 | |
223 | static ssize_t write_file_beacon(struct file *file, |
224 | const char __user *userbuf, |
225 | size_t count, loff_t *ppos) |
226 | { |
227 | struct ath5k_hw *ah = file->private_data; |
228 | char buf[20]; |
229 | |
230 | count = min_t(size_t, count, sizeof(buf) - 1); |
231 | if (copy_from_user(to: buf, from: userbuf, n: count)) |
232 | return -EFAULT; |
233 | |
234 | buf[count] = '\0'; |
235 | if (strncmp(buf, "disable" , 7) == 0) { |
236 | AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); |
237 | pr_info("debugfs disable beacons\n" ); |
238 | } else if (strncmp(buf, "enable" , 6) == 0) { |
239 | AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); |
240 | pr_info("debugfs enable beacons\n" ); |
241 | } |
242 | return count; |
243 | } |
244 | |
245 | static const struct file_operations fops_beacon = { |
246 | .read = read_file_beacon, |
247 | .write = write_file_beacon, |
248 | .open = simple_open, |
249 | .owner = THIS_MODULE, |
250 | .llseek = default_llseek, |
251 | }; |
252 | |
253 | |
254 | /* debugfs: reset */ |
255 | |
256 | static ssize_t write_file_reset(struct file *file, |
257 | const char __user *userbuf, |
258 | size_t count, loff_t *ppos) |
259 | { |
260 | struct ath5k_hw *ah = file->private_data; |
261 | ATH5K_DBG(ah, ATH5K_DEBUG_RESET, "debug file triggered reset\n" ); |
262 | ieee80211_queue_work(hw: ah->hw, work: &ah->reset_work); |
263 | return count; |
264 | } |
265 | |
266 | static const struct file_operations fops_reset = { |
267 | .write = write_file_reset, |
268 | .open = simple_open, |
269 | .owner = THIS_MODULE, |
270 | .llseek = noop_llseek, |
271 | }; |
272 | |
273 | |
274 | /* debugfs: debug level */ |
275 | |
276 | static const struct { |
277 | enum ath5k_debug_level level; |
278 | const char *name; |
279 | const char *desc; |
280 | } dbg_info[] = { |
281 | { ATH5K_DEBUG_RESET, "reset" , "reset and initialization" }, |
282 | { ATH5K_DEBUG_INTR, "intr" , "interrupt handling" }, |
283 | { ATH5K_DEBUG_MODE, "mode" , "mode init/setup" }, |
284 | { ATH5K_DEBUG_XMIT, "xmit" , "basic xmit operation" }, |
285 | { ATH5K_DEBUG_BEACON, "beacon" , "beacon handling" }, |
286 | { ATH5K_DEBUG_CALIBRATE, "calib" , "periodic calibration" }, |
287 | { ATH5K_DEBUG_TXPOWER, "txpower" , "transmit power setting" }, |
288 | { ATH5K_DEBUG_LED, "led" , "LED management" }, |
289 | { ATH5K_DEBUG_DUMPBANDS, "dumpbands" , "dump bands" }, |
290 | { ATH5K_DEBUG_DMA, "dma" , "dma start/stop" }, |
291 | { ATH5K_DEBUG_ANI, "ani" , "adaptive noise immunity" }, |
292 | { ATH5K_DEBUG_DESC, "desc" , "descriptor chains" }, |
293 | { ATH5K_DEBUG_ANY, "all" , "show all debug levels" }, |
294 | }; |
295 | |
296 | static ssize_t read_file_debug(struct file *file, char __user *user_buf, |
297 | size_t count, loff_t *ppos) |
298 | { |
299 | struct ath5k_hw *ah = file->private_data; |
300 | char buf[700]; |
301 | unsigned int len = 0; |
302 | unsigned int i; |
303 | |
304 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
305 | fmt: "DEBUG LEVEL: 0x%08x\n\n" , ah->debug.level); |
306 | |
307 | for (i = 0; i < ARRAY_SIZE(dbg_info) - 1; i++) { |
308 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
309 | fmt: "%10s %c 0x%08x - %s\n" , dbg_info[i].name, |
310 | ah->debug.level & dbg_info[i].level ? '+' : ' ', |
311 | dbg_info[i].level, dbg_info[i].desc); |
312 | } |
313 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
314 | fmt: "%10s %c 0x%08x - %s\n" , dbg_info[i].name, |
315 | ah->debug.level == dbg_info[i].level ? '+' : ' ', |
316 | dbg_info[i].level, dbg_info[i].desc); |
317 | |
318 | if (len > sizeof(buf)) |
319 | len = sizeof(buf); |
320 | |
321 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
322 | } |
323 | |
324 | static ssize_t write_file_debug(struct file *file, |
325 | const char __user *userbuf, |
326 | size_t count, loff_t *ppos) |
327 | { |
328 | struct ath5k_hw *ah = file->private_data; |
329 | unsigned int i; |
330 | char buf[20]; |
331 | |
332 | count = min_t(size_t, count, sizeof(buf) - 1); |
333 | if (copy_from_user(to: buf, from: userbuf, n: count)) |
334 | return -EFAULT; |
335 | |
336 | buf[count] = '\0'; |
337 | for (i = 0; i < ARRAY_SIZE(dbg_info); i++) { |
338 | if (strncmp(buf, dbg_info[i].name, |
339 | strlen(dbg_info[i].name)) == 0) { |
340 | ah->debug.level ^= dbg_info[i].level; /* toggle bit */ |
341 | break; |
342 | } |
343 | } |
344 | return count; |
345 | } |
346 | |
347 | static const struct file_operations fops_debug = { |
348 | .read = read_file_debug, |
349 | .write = write_file_debug, |
350 | .open = simple_open, |
351 | .owner = THIS_MODULE, |
352 | .llseek = default_llseek, |
353 | }; |
354 | |
355 | |
356 | /* debugfs: antenna */ |
357 | |
358 | static ssize_t read_file_antenna(struct file *file, char __user *user_buf, |
359 | size_t count, loff_t *ppos) |
360 | { |
361 | struct ath5k_hw *ah = file->private_data; |
362 | char buf[700]; |
363 | unsigned int len = 0; |
364 | unsigned int i; |
365 | unsigned int v; |
366 | |
367 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "antenna mode\t%d\n" , |
368 | ah->ah_ant_mode); |
369 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "default antenna\t%d\n" , |
370 | ah->ah_def_ant); |
371 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "tx antenna\t%d\n" , |
372 | ah->ah_tx_ant); |
373 | |
374 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "\nANTENNA\t\tRX\tTX\n" ); |
375 | for (i = 1; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) { |
376 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
377 | fmt: "[antenna %d]\t%d\t%d\n" , |
378 | i, ah->stats.antenna_rx[i], ah->stats.antenna_tx[i]); |
379 | } |
380 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "[invalid]\t%d\t%d\n" , |
381 | ah->stats.antenna_rx[0], ah->stats.antenna_tx[0]); |
382 | |
383 | v = ath5k_hw_reg_read(ah, AR5K_DEFAULT_ANTENNA); |
384 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
385 | fmt: "\nAR5K_DEFAULT_ANTENNA\t0x%08x\n" , v); |
386 | |
387 | v = ath5k_hw_reg_read(ah, AR5K_STA_ID1); |
388 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
389 | fmt: "AR5K_STA_ID1_DEFAULT_ANTENNA\t%d\n" , |
390 | (v & AR5K_STA_ID1_DEFAULT_ANTENNA) != 0); |
391 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
392 | fmt: "AR5K_STA_ID1_DESC_ANTENNA\t%d\n" , |
393 | (v & AR5K_STA_ID1_DESC_ANTENNA) != 0); |
394 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
395 | fmt: "AR5K_STA_ID1_RTS_DEF_ANTENNA\t%d\n" , |
396 | (v & AR5K_STA_ID1_RTS_DEF_ANTENNA) != 0); |
397 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
398 | fmt: "AR5K_STA_ID1_SELFGEN_DEF_ANT\t%d\n" , |
399 | (v & AR5K_STA_ID1_SELFGEN_DEF_ANT) != 0); |
400 | |
401 | v = ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL); |
402 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
403 | fmt: "\nAR5K_PHY_AGCCTL_OFDM_DIV_DIS\t%d\n" , |
404 | (v & AR5K_PHY_AGCCTL_OFDM_DIV_DIS) != 0); |
405 | |
406 | v = ath5k_hw_reg_read(ah, AR5K_PHY_RESTART); |
407 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
408 | fmt: "AR5K_PHY_RESTART_DIV_GC\t\t%x\n" , |
409 | (v & AR5K_PHY_RESTART_DIV_GC) >> AR5K_PHY_RESTART_DIV_GC_S); |
410 | |
411 | v = ath5k_hw_reg_read(ah, AR5K_PHY_FAST_ANT_DIV); |
412 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
413 | fmt: "AR5K_PHY_FAST_ANT_DIV_EN\t%d\n" , |
414 | (v & AR5K_PHY_FAST_ANT_DIV_EN) != 0); |
415 | |
416 | v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_0); |
417 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
418 | fmt: "\nAR5K_PHY_ANT_SWITCH_TABLE_0\t0x%08x\n" , v); |
419 | v = ath5k_hw_reg_read(ah, AR5K_PHY_ANT_SWITCH_TABLE_1); |
420 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
421 | fmt: "AR5K_PHY_ANT_SWITCH_TABLE_1\t0x%08x\n" , v); |
422 | |
423 | if (len > sizeof(buf)) |
424 | len = sizeof(buf); |
425 | |
426 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
427 | } |
428 | |
429 | static ssize_t write_file_antenna(struct file *file, |
430 | const char __user *userbuf, |
431 | size_t count, loff_t *ppos) |
432 | { |
433 | struct ath5k_hw *ah = file->private_data; |
434 | unsigned int i; |
435 | char buf[20]; |
436 | |
437 | count = min_t(size_t, count, sizeof(buf) - 1); |
438 | if (copy_from_user(to: buf, from: userbuf, n: count)) |
439 | return -EFAULT; |
440 | |
441 | buf[count] = '\0'; |
442 | if (strncmp(buf, "diversity" , 9) == 0) { |
443 | ath5k_hw_set_antenna_mode(ah, ant_mode: AR5K_ANTMODE_DEFAULT); |
444 | pr_info("debug: enable diversity\n" ); |
445 | } else if (strncmp(buf, "fixed-a" , 7) == 0) { |
446 | ath5k_hw_set_antenna_mode(ah, ant_mode: AR5K_ANTMODE_FIXED_A); |
447 | pr_info("debug: fixed antenna A\n" ); |
448 | } else if (strncmp(buf, "fixed-b" , 7) == 0) { |
449 | ath5k_hw_set_antenna_mode(ah, ant_mode: AR5K_ANTMODE_FIXED_B); |
450 | pr_info("debug: fixed antenna B\n" ); |
451 | } else if (strncmp(buf, "clear" , 5) == 0) { |
452 | for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) { |
453 | ah->stats.antenna_rx[i] = 0; |
454 | ah->stats.antenna_tx[i] = 0; |
455 | } |
456 | pr_info("debug: cleared antenna stats\n" ); |
457 | } |
458 | return count; |
459 | } |
460 | |
461 | static const struct file_operations fops_antenna = { |
462 | .read = read_file_antenna, |
463 | .write = write_file_antenna, |
464 | .open = simple_open, |
465 | .owner = THIS_MODULE, |
466 | .llseek = default_llseek, |
467 | }; |
468 | |
469 | /* debugfs: misc */ |
470 | |
471 | static ssize_t read_file_misc(struct file *file, char __user *user_buf, |
472 | size_t count, loff_t *ppos) |
473 | { |
474 | struct ath5k_hw *ah = file->private_data; |
475 | char buf[700]; |
476 | unsigned int len = 0; |
477 | u32 filt = ath5k_hw_get_rx_filter(ah); |
478 | |
479 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "bssid-mask: %pM\n" , |
480 | ah->bssidmask); |
481 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "filter-flags: 0x%x " , |
482 | filt); |
483 | if (filt & AR5K_RX_FILTER_UCAST) |
484 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " UCAST" ); |
485 | if (filt & AR5K_RX_FILTER_MCAST) |
486 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " MCAST" ); |
487 | if (filt & AR5K_RX_FILTER_BCAST) |
488 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " BCAST" ); |
489 | if (filt & AR5K_RX_FILTER_CONTROL) |
490 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " CONTROL" ); |
491 | if (filt & AR5K_RX_FILTER_BEACON) |
492 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " BEACON" ); |
493 | if (filt & AR5K_RX_FILTER_PROM) |
494 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " PROM" ); |
495 | if (filt & AR5K_RX_FILTER_XRPOLL) |
496 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " XRPOLL" ); |
497 | if (filt & AR5K_RX_FILTER_PROBEREQ) |
498 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " PROBEREQ" ); |
499 | if (filt & AR5K_RX_FILTER_PHYERR_5212) |
500 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " PHYERR-5212" ); |
501 | if (filt & AR5K_RX_FILTER_RADARERR_5212) |
502 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " RADARERR-5212" ); |
503 | if (filt & AR5K_RX_FILTER_PHYERR_5211) |
504 | snprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " PHYERR-5211" ); |
505 | if (filt & AR5K_RX_FILTER_RADARERR_5211) |
506 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: " RADARERR-5211" ); |
507 | |
508 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "\nopmode: %s (%d)\n" , |
509 | ath_opmode_to_string(opmode: ah->opmode), ah->opmode); |
510 | |
511 | if (len > sizeof(buf)) |
512 | len = sizeof(buf); |
513 | |
514 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
515 | } |
516 | |
517 | static const struct file_operations fops_misc = { |
518 | .read = read_file_misc, |
519 | .open = simple_open, |
520 | .owner = THIS_MODULE, |
521 | }; |
522 | |
523 | |
524 | /* debugfs: frameerrors */ |
525 | |
526 | static ssize_t read_file_frameerrors(struct file *file, char __user *user_buf, |
527 | size_t count, loff_t *ppos) |
528 | { |
529 | struct ath5k_hw *ah = file->private_data; |
530 | struct ath5k_statistics *st = &ah->stats; |
531 | char buf[700]; |
532 | unsigned int len = 0; |
533 | int i; |
534 | |
535 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
536 | fmt: "RX\n---------------------\n" ); |
537 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "CRC\t%u\t(%u%%)\n" , |
538 | st->rxerr_crc, |
539 | st->rx_all_count > 0 ? |
540 | st->rxerr_crc * 100 / st->rx_all_count : 0); |
541 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "PHY\t%u\t(%u%%)\n" , |
542 | st->rxerr_phy, |
543 | st->rx_all_count > 0 ? |
544 | st->rxerr_phy * 100 / st->rx_all_count : 0); |
545 | for (i = 0; i < 32; i++) { |
546 | if (st->rxerr_phy_code[i]) |
547 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
548 | fmt: " phy_err[%u]\t%u\n" , |
549 | i, st->rxerr_phy_code[i]); |
550 | } |
551 | |
552 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "FIFO\t%u\t(%u%%)\n" , |
553 | st->rxerr_fifo, |
554 | st->rx_all_count > 0 ? |
555 | st->rxerr_fifo * 100 / st->rx_all_count : 0); |
556 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "decrypt\t%u\t(%u%%)\n" , |
557 | st->rxerr_decrypt, |
558 | st->rx_all_count > 0 ? |
559 | st->rxerr_decrypt * 100 / st->rx_all_count : 0); |
560 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "MIC\t%u\t(%u%%)\n" , |
561 | st->rxerr_mic, |
562 | st->rx_all_count > 0 ? |
563 | st->rxerr_mic * 100 / st->rx_all_count : 0); |
564 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "process\t%u\t(%u%%)\n" , |
565 | st->rxerr_proc, |
566 | st->rx_all_count > 0 ? |
567 | st->rxerr_proc * 100 / st->rx_all_count : 0); |
568 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "jumbo\t%u\t(%u%%)\n" , |
569 | st->rxerr_jumbo, |
570 | st->rx_all_count > 0 ? |
571 | st->rxerr_jumbo * 100 / st->rx_all_count : 0); |
572 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "[RX all\t%u]\n" , |
573 | st->rx_all_count); |
574 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "RX-all-bytes\t%u\n" , |
575 | st->rx_bytes_count); |
576 | |
577 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
578 | fmt: "\nTX\n---------------------\n" ); |
579 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "retry\t%u\t(%u%%)\n" , |
580 | st->txerr_retry, |
581 | st->tx_all_count > 0 ? |
582 | st->txerr_retry * 100 / st->tx_all_count : 0); |
583 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "FIFO\t%u\t(%u%%)\n" , |
584 | st->txerr_fifo, |
585 | st->tx_all_count > 0 ? |
586 | st->txerr_fifo * 100 / st->tx_all_count : 0); |
587 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "filter\t%u\t(%u%%)\n" , |
588 | st->txerr_filt, |
589 | st->tx_all_count > 0 ? |
590 | st->txerr_filt * 100 / st->tx_all_count : 0); |
591 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "[TX all\t%u]\n" , |
592 | st->tx_all_count); |
593 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "TX-all-bytes\t%u\n" , |
594 | st->tx_bytes_count); |
595 | |
596 | if (len > sizeof(buf)) |
597 | len = sizeof(buf); |
598 | |
599 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
600 | } |
601 | |
602 | static ssize_t write_file_frameerrors(struct file *file, |
603 | const char __user *userbuf, |
604 | size_t count, loff_t *ppos) |
605 | { |
606 | struct ath5k_hw *ah = file->private_data; |
607 | struct ath5k_statistics *st = &ah->stats; |
608 | char buf[20]; |
609 | |
610 | count = min_t(size_t, count, sizeof(buf) - 1); |
611 | if (copy_from_user(to: buf, from: userbuf, n: count)) |
612 | return -EFAULT; |
613 | |
614 | buf[count] = '\0'; |
615 | if (strncmp(buf, "clear" , 5) == 0) { |
616 | st->rxerr_crc = 0; |
617 | st->rxerr_phy = 0; |
618 | st->rxerr_fifo = 0; |
619 | st->rxerr_decrypt = 0; |
620 | st->rxerr_mic = 0; |
621 | st->rxerr_proc = 0; |
622 | st->rxerr_jumbo = 0; |
623 | st->rx_all_count = 0; |
624 | st->txerr_retry = 0; |
625 | st->txerr_fifo = 0; |
626 | st->txerr_filt = 0; |
627 | st->tx_all_count = 0; |
628 | pr_info("debug: cleared frameerrors stats\n" ); |
629 | } |
630 | return count; |
631 | } |
632 | |
633 | static const struct file_operations fops_frameerrors = { |
634 | .read = read_file_frameerrors, |
635 | .write = write_file_frameerrors, |
636 | .open = simple_open, |
637 | .owner = THIS_MODULE, |
638 | .llseek = default_llseek, |
639 | }; |
640 | |
641 | |
642 | /* debugfs: ani */ |
643 | |
644 | static ssize_t read_file_ani(struct file *file, char __user *user_buf, |
645 | size_t count, loff_t *ppos) |
646 | { |
647 | struct ath5k_hw *ah = file->private_data; |
648 | struct ath5k_statistics *st = &ah->stats; |
649 | struct ath5k_ani_state *as = &ah->ani_state; |
650 | |
651 | char buf[700]; |
652 | unsigned int len = 0; |
653 | |
654 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
655 | fmt: "HW has PHY error counters:\t%s\n" , |
656 | ah->ah_capabilities.cap_has_phyerr_counters ? |
657 | "yes" : "no" ); |
658 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
659 | fmt: "HW max spur immunity level:\t%d\n" , |
660 | as->max_spur_level); |
661 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
662 | fmt: "\nANI state\n--------------------------------------------\n" ); |
663 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "operating mode:\t\t\t" ); |
664 | switch (as->ani_mode) { |
665 | case ATH5K_ANI_MODE_OFF: |
666 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "OFF\n" ); |
667 | break; |
668 | case ATH5K_ANI_MODE_MANUAL_LOW: |
669 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
670 | fmt: "MANUAL LOW\n" ); |
671 | break; |
672 | case ATH5K_ANI_MODE_MANUAL_HIGH: |
673 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
674 | fmt: "MANUAL HIGH\n" ); |
675 | break; |
676 | case ATH5K_ANI_MODE_AUTO: |
677 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "AUTO\n" ); |
678 | break; |
679 | default: |
680 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
681 | fmt: "??? (not good)\n" ); |
682 | break; |
683 | } |
684 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
685 | fmt: "noise immunity level:\t\t%d\n" , |
686 | as->noise_imm_level); |
687 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
688 | fmt: "spur immunity level:\t\t%d\n" , |
689 | as->spur_level); |
690 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
691 | fmt: "firstep level:\t\t\t%d\n" , |
692 | as->firstep_level); |
693 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
694 | fmt: "OFDM weak signal detection:\t%s\n" , |
695 | as->ofdm_weak_sig ? "on" : "off" ); |
696 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
697 | fmt: "CCK weak signal detection:\t%s\n" , |
698 | as->cck_weak_sig ? "on" : "off" ); |
699 | |
700 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
701 | fmt: "\nMIB INTERRUPTS:\t\t%u\n" , |
702 | st->mib_intr); |
703 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
704 | fmt: "beacon RSSI average:\t%d\n" , |
705 | (int)ewma_beacon_rssi_read(e: &ah->ah_beacon_rssi_avg)); |
706 | |
707 | #define CC_PRINT(_struct, _field) \ |
708 | _struct._field, \ |
709 | _struct.cycles > 0 ? \ |
710 | _struct._field * 100 / _struct.cycles : 0 |
711 | |
712 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
713 | fmt: "profcnt tx\t\t%u\t(%d%%)\n" , |
714 | CC_PRINT(as->last_cc, tx_frame)); |
715 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
716 | fmt: "profcnt rx\t\t%u\t(%d%%)\n" , |
717 | CC_PRINT(as->last_cc, rx_frame)); |
718 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
719 | fmt: "profcnt busy\t\t%u\t(%d%%)\n" , |
720 | CC_PRINT(as->last_cc, rx_busy)); |
721 | #undef CC_PRINT |
722 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, fmt: "profcnt cycles\t\t%u\n" , |
723 | as->last_cc.cycles); |
724 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
725 | fmt: "listen time\t\t%d\tlast: %d\n" , |
726 | as->listen_time, as->last_listen); |
727 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
728 | fmt: "OFDM errors\t\t%u\tlast: %u\tsum: %u\n" , |
729 | as->ofdm_errors, as->last_ofdm_errors, |
730 | as->sum_ofdm_errors); |
731 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
732 | fmt: "CCK errors\t\t%u\tlast: %u\tsum: %u\n" , |
733 | as->cck_errors, as->last_cck_errors, |
734 | as->sum_cck_errors); |
735 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
736 | fmt: "AR5K_PHYERR_CNT1\t%x\t(=%d)\n" , |
737 | ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1), |
738 | ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - |
739 | ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1))); |
740 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
741 | fmt: "AR5K_PHYERR_CNT2\t%x\t(=%d)\n" , |
742 | ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2), |
743 | ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - |
744 | ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2))); |
745 | |
746 | if (len > sizeof(buf)) |
747 | len = sizeof(buf); |
748 | |
749 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
750 | } |
751 | |
752 | static ssize_t write_file_ani(struct file *file, |
753 | const char __user *userbuf, |
754 | size_t count, loff_t *ppos) |
755 | { |
756 | struct ath5k_hw *ah = file->private_data; |
757 | char buf[20]; |
758 | |
759 | count = min_t(size_t, count, sizeof(buf) - 1); |
760 | if (copy_from_user(to: buf, from: userbuf, n: count)) |
761 | return -EFAULT; |
762 | |
763 | buf[count] = '\0'; |
764 | if (strncmp(buf, "sens-low" , 8) == 0) { |
765 | ath5k_ani_init(ah, mode: ATH5K_ANI_MODE_MANUAL_HIGH); |
766 | } else if (strncmp(buf, "sens-high" , 9) == 0) { |
767 | ath5k_ani_init(ah, mode: ATH5K_ANI_MODE_MANUAL_LOW); |
768 | } else if (strncmp(buf, "ani-off" , 7) == 0) { |
769 | ath5k_ani_init(ah, mode: ATH5K_ANI_MODE_OFF); |
770 | } else if (strncmp(buf, "ani-on" , 6) == 0) { |
771 | ath5k_ani_init(ah, mode: ATH5K_ANI_MODE_AUTO); |
772 | } else if (strncmp(buf, "noise-low" , 9) == 0) { |
773 | ath5k_ani_set_noise_immunity_level(ah, level: 0); |
774 | } else if (strncmp(buf, "noise-high" , 10) == 0) { |
775 | ath5k_ani_set_noise_immunity_level(ah, |
776 | ATH5K_ANI_MAX_NOISE_IMM_LVL); |
777 | } else if (strncmp(buf, "spur-low" , 8) == 0) { |
778 | ath5k_ani_set_spur_immunity_level(ah, level: 0); |
779 | } else if (strncmp(buf, "spur-high" , 9) == 0) { |
780 | ath5k_ani_set_spur_immunity_level(ah, |
781 | level: ah->ani_state.max_spur_level); |
782 | } else if (strncmp(buf, "fir-low" , 7) == 0) { |
783 | ath5k_ani_set_firstep_level(ah, level: 0); |
784 | } else if (strncmp(buf, "fir-high" , 8) == 0) { |
785 | ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL); |
786 | } else if (strncmp(buf, "ofdm-off" , 8) == 0) { |
787 | ath5k_ani_set_ofdm_weak_signal_detection(ah, on: false); |
788 | } else if (strncmp(buf, "ofdm-on" , 7) == 0) { |
789 | ath5k_ani_set_ofdm_weak_signal_detection(ah, on: true); |
790 | } else if (strncmp(buf, "cck-off" , 7) == 0) { |
791 | ath5k_ani_set_cck_weak_signal_detection(ah, on: false); |
792 | } else if (strncmp(buf, "cck-on" , 6) == 0) { |
793 | ath5k_ani_set_cck_weak_signal_detection(ah, on: true); |
794 | } |
795 | return count; |
796 | } |
797 | |
798 | static const struct file_operations fops_ani = { |
799 | .read = read_file_ani, |
800 | .write = write_file_ani, |
801 | .open = simple_open, |
802 | .owner = THIS_MODULE, |
803 | .llseek = default_llseek, |
804 | }; |
805 | |
806 | |
807 | /* debugfs: queues etc */ |
808 | |
809 | static ssize_t read_file_queue(struct file *file, char __user *user_buf, |
810 | size_t count, loff_t *ppos) |
811 | { |
812 | struct ath5k_hw *ah = file->private_data; |
813 | char buf[700]; |
814 | unsigned int len = 0; |
815 | |
816 | struct ath5k_txq *txq; |
817 | struct ath5k_buf *bf, *bf0; |
818 | int i, n; |
819 | |
820 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
821 | fmt: "available txbuffers: %d\n" , ah->txbuf_len); |
822 | |
823 | for (i = 0; i < ARRAY_SIZE(ah->txqs); i++) { |
824 | txq = &ah->txqs[i]; |
825 | |
826 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
827 | fmt: "%02d: %ssetup\n" , i, txq->setup ? "" : "not " ); |
828 | |
829 | if (!txq->setup) |
830 | continue; |
831 | |
832 | n = 0; |
833 | spin_lock_bh(lock: &txq->lock); |
834 | list_for_each_entry_safe(bf, bf0, &txq->q, list) |
835 | n++; |
836 | spin_unlock_bh(lock: &txq->lock); |
837 | |
838 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
839 | fmt: " len: %d bufs: %d\n" , txq->txq_len, n); |
840 | len += scnprintf(buf: buf + len, size: sizeof(buf) - len, |
841 | fmt: " stuck: %d\n" , txq->txq_stuck); |
842 | } |
843 | |
844 | if (len > sizeof(buf)) |
845 | len = sizeof(buf); |
846 | |
847 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
848 | } |
849 | |
850 | static ssize_t write_file_queue(struct file *file, |
851 | const char __user *userbuf, |
852 | size_t count, loff_t *ppos) |
853 | { |
854 | struct ath5k_hw *ah = file->private_data; |
855 | char buf[20]; |
856 | |
857 | count = min_t(size_t, count, sizeof(buf) - 1); |
858 | if (copy_from_user(to: buf, from: userbuf, n: count)) |
859 | return -EFAULT; |
860 | |
861 | buf[count] = '\0'; |
862 | if (strncmp(buf, "start" , 5) == 0) |
863 | ieee80211_wake_queues(hw: ah->hw); |
864 | else if (strncmp(buf, "stop" , 4) == 0) |
865 | ieee80211_stop_queues(hw: ah->hw); |
866 | |
867 | return count; |
868 | } |
869 | |
870 | |
871 | static const struct file_operations fops_queue = { |
872 | .read = read_file_queue, |
873 | .write = write_file_queue, |
874 | .open = simple_open, |
875 | .owner = THIS_MODULE, |
876 | .llseek = default_llseek, |
877 | }; |
878 | |
879 | /* debugfs: eeprom */ |
880 | |
881 | struct eeprom_private { |
882 | u16 *buf; |
883 | int len; |
884 | }; |
885 | |
886 | static int open_file_eeprom(struct inode *inode, struct file *file) |
887 | { |
888 | struct eeprom_private *ep; |
889 | struct ath5k_hw *ah = inode->i_private; |
890 | bool res; |
891 | int i, ret; |
892 | u32 eesize; /* NB: in 16-bit words */ |
893 | u16 val, *buf; |
894 | |
895 | /* Get eeprom size */ |
896 | |
897 | res = ath5k_hw_nvram_read(ah, AR5K_EEPROM_SIZE_UPPER, data: &val); |
898 | if (!res) |
899 | return -EACCES; |
900 | |
901 | if (val == 0) { |
902 | eesize = AR5K_EEPROM_INFO_MAX + AR5K_EEPROM_INFO_BASE; |
903 | } else { |
904 | eesize = (val & AR5K_EEPROM_SIZE_UPPER_MASK) << |
905 | AR5K_EEPROM_SIZE_ENDLOC_SHIFT; |
906 | ath5k_hw_nvram_read(ah, AR5K_EEPROM_SIZE_LOWER, data: &val); |
907 | eesize = eesize | val; |
908 | } |
909 | |
910 | if (eesize > 4096) |
911 | return -EINVAL; |
912 | |
913 | /* Create buffer and read in eeprom */ |
914 | |
915 | buf = vmalloc(array_size(eesize, 2)); |
916 | if (!buf) { |
917 | ret = -ENOMEM; |
918 | goto err; |
919 | } |
920 | |
921 | for (i = 0; i < eesize; ++i) { |
922 | if (!ath5k_hw_nvram_read(ah, off: i, data: &val)) { |
923 | ret = -EIO; |
924 | goto freebuf; |
925 | } |
926 | buf[i] = val; |
927 | } |
928 | |
929 | /* Create private struct and assign to file */ |
930 | |
931 | ep = kmalloc(size: sizeof(*ep), GFP_KERNEL); |
932 | if (!ep) { |
933 | ret = -ENOMEM; |
934 | goto freebuf; |
935 | } |
936 | |
937 | ep->buf = buf; |
938 | ep->len = eesize * 2; |
939 | |
940 | file->private_data = (void *)ep; |
941 | |
942 | return 0; |
943 | |
944 | freebuf: |
945 | vfree(addr: buf); |
946 | err: |
947 | return ret; |
948 | |
949 | } |
950 | |
951 | static ssize_t read_file_eeprom(struct file *file, char __user *user_buf, |
952 | size_t count, loff_t *ppos) |
953 | { |
954 | struct eeprom_private *ep = file->private_data; |
955 | |
956 | return simple_read_from_buffer(to: user_buf, count, ppos, from: ep->buf, available: ep->len); |
957 | } |
958 | |
959 | static int release_file_eeprom(struct inode *inode, struct file *file) |
960 | { |
961 | struct eeprom_private *ep = file->private_data; |
962 | |
963 | vfree(addr: ep->buf); |
964 | kfree(objp: ep); |
965 | |
966 | return 0; |
967 | } |
968 | |
969 | static const struct file_operations fops_eeprom = { |
970 | .open = open_file_eeprom, |
971 | .read = read_file_eeprom, |
972 | .release = release_file_eeprom, |
973 | .owner = THIS_MODULE, |
974 | }; |
975 | |
976 | |
977 | void |
978 | ath5k_debug_init_device(struct ath5k_hw *ah) |
979 | { |
980 | struct dentry *phydir; |
981 | |
982 | ah->debug.level = ath5k_debug; |
983 | |
984 | phydir = debugfs_create_dir(name: "ath5k" , parent: ah->hw->wiphy->debugfsdir); |
985 | |
986 | debugfs_create_file(name: "debug" , mode: 0600, parent: phydir, data: ah, fops: &fops_debug); |
987 | debugfs_create_file(name: "registers" , mode: 0400, parent: phydir, data: ah, fops: ®isters_fops); |
988 | debugfs_create_file(name: "beacon" , mode: 0600, parent: phydir, data: ah, fops: &fops_beacon); |
989 | debugfs_create_file(name: "reset" , mode: 0200, parent: phydir, data: ah, fops: &fops_reset); |
990 | debugfs_create_file(name: "antenna" , mode: 0600, parent: phydir, data: ah, fops: &fops_antenna); |
991 | debugfs_create_file(name: "misc" , mode: 0400, parent: phydir, data: ah, fops: &fops_misc); |
992 | debugfs_create_file(name: "eeprom" , mode: 0400, parent: phydir, data: ah, fops: &fops_eeprom); |
993 | debugfs_create_file(name: "frameerrors" , mode: 0600, parent: phydir, data: ah, fops: &fops_frameerrors); |
994 | debugfs_create_file(name: "ani" , mode: 0600, parent: phydir, data: ah, fops: &fops_ani); |
995 | debugfs_create_file(name: "queue" , mode: 0600, parent: phydir, data: ah, fops: &fops_queue); |
996 | debugfs_create_bool(name: "32khz_clock" , mode: 0600, parent: phydir, |
997 | value: &ah->ah_use_32khz_clock); |
998 | } |
999 | |
1000 | /* functions used in other places */ |
1001 | |
1002 | void |
1003 | ath5k_debug_dump_bands(struct ath5k_hw *ah) |
1004 | { |
1005 | unsigned int b, i; |
1006 | |
1007 | if (likely(!(ah->debug.level & ATH5K_DEBUG_DUMPBANDS))) |
1008 | return; |
1009 | |
1010 | for (b = 0; b < NUM_NL80211_BANDS; b++) { |
1011 | struct ieee80211_supported_band *band = &ah->sbands[b]; |
1012 | char bname[6]; |
1013 | switch (band->band) { |
1014 | case NL80211_BAND_2GHZ: |
1015 | strcpy(p: bname, q: "2 GHz" ); |
1016 | break; |
1017 | case NL80211_BAND_5GHZ: |
1018 | strcpy(p: bname, q: "5 GHz" ); |
1019 | break; |
1020 | default: |
1021 | printk(KERN_DEBUG "Band not supported: %d\n" , |
1022 | band->band); |
1023 | return; |
1024 | } |
1025 | printk(KERN_DEBUG "Band %s: channels %d, rates %d\n" , bname, |
1026 | band->n_channels, band->n_bitrates); |
1027 | printk(KERN_DEBUG " channels:\n" ); |
1028 | for (i = 0; i < band->n_channels; i++) |
1029 | printk(KERN_DEBUG " %3d %d %.4x %.4x\n" , |
1030 | ieee80211_frequency_to_channel( |
1031 | band->channels[i].center_freq), |
1032 | band->channels[i].center_freq, |
1033 | band->channels[i].hw_value, |
1034 | band->channels[i].flags); |
1035 | printk(KERN_DEBUG " rates:\n" ); |
1036 | for (i = 0; i < band->n_bitrates; i++) |
1037 | printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n" , |
1038 | band->bitrates[i].bitrate, |
1039 | band->bitrates[i].hw_value, |
1040 | band->bitrates[i].flags, |
1041 | band->bitrates[i].hw_value_short); |
1042 | } |
1043 | } |
1044 | |
1045 | static inline void |
1046 | ath5k_debug_printrxbuf(struct ath5k_buf *bf, int done, |
1047 | struct ath5k_rx_status *rs) |
1048 | { |
1049 | struct ath5k_desc *ds = bf->desc; |
1050 | struct ath5k_hw_all_rx_desc *rd = &ds->ud.ds_rx; |
1051 | |
1052 | printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n" , |
1053 | ds, (unsigned long long)bf->daddr, |
1054 | ds->ds_link, ds->ds_data, |
1055 | rd->rx_ctl.rx_control_0, rd->rx_ctl.rx_control_1, |
1056 | rd->rx_stat.rx_status_0, rd->rx_stat.rx_status_1, |
1057 | !done ? ' ' : (rs->rs_status == 0) ? '*' : '!'); |
1058 | } |
1059 | |
1060 | void |
1061 | ath5k_debug_printrxbuffs(struct ath5k_hw *ah) |
1062 | { |
1063 | struct ath5k_desc *ds; |
1064 | struct ath5k_buf *bf; |
1065 | struct ath5k_rx_status rs = {}; |
1066 | int status; |
1067 | |
1068 | if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC))) |
1069 | return; |
1070 | |
1071 | printk(KERN_DEBUG "rxdp %x, rxlink %p\n" , |
1072 | ath5k_hw_get_rxdp(ah), ah->rxlink); |
1073 | |
1074 | spin_lock_bh(lock: &ah->rxbuflock); |
1075 | list_for_each_entry(bf, &ah->rxbuf, list) { |
1076 | ds = bf->desc; |
1077 | status = ah->ah_proc_rx_desc(ah, ds, &rs); |
1078 | if (!status) |
1079 | ath5k_debug_printrxbuf(bf, done: status == 0, rs: &rs); |
1080 | } |
1081 | spin_unlock_bh(lock: &ah->rxbuflock); |
1082 | } |
1083 | |
1084 | void |
1085 | ath5k_debug_printtxbuf(struct ath5k_hw *ah, struct ath5k_buf *bf) |
1086 | { |
1087 | struct ath5k_desc *ds = bf->desc; |
1088 | struct ath5k_hw_5212_tx_desc *td = &ds->ud.ds_tx5212; |
1089 | struct ath5k_tx_status ts = {}; |
1090 | int done; |
1091 | |
1092 | if (likely(!(ah->debug.level & ATH5K_DEBUG_DESC))) |
1093 | return; |
1094 | |
1095 | done = ah->ah_proc_tx_desc(ah, bf->desc, &ts); |
1096 | |
1097 | printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x " |
1098 | "%08x %c\n" , ds, (unsigned long long)bf->daddr, ds->ds_link, |
1099 | ds->ds_data, td->tx_ctl.tx_control_0, td->tx_ctl.tx_control_1, |
1100 | td->tx_ctl.tx_control_2, td->tx_ctl.tx_control_3, |
1101 | td->tx_stat.tx_status_0, td->tx_stat.tx_status_1, |
1102 | done ? ' ' : (ts.ts_status == 0) ? '*' : '!'); |
1103 | } |
1104 | |