2 * Copyright (c) 2008-2009 Atheros Communications Inc.
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.
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.
17 #include <asm/unaligned.h>
21 static unsigned int ath9k_debug
= DBG_DEFAULT
;
22 module_param_named(debug
, ath9k_debug
, uint
, 0);
24 static struct dentry
*ath9k_debugfs_root
;
26 void DPRINTF(struct ath_softc
*sc
, int dbg_mask
, const char *fmt
, ...)
31 if (sc
->debug
.debug_mask
& dbg_mask
) {
35 printk(KERN_DEBUG
"ath9k: ");
41 static int ath9k_debugfs_open(struct inode
*inode
, struct file
*file
)
43 file
->private_data
= inode
->i_private
;
47 static ssize_t
read_file_dma(struct file
*file
, char __user
*user_buf
,
48 size_t count
, loff_t
*ppos
)
50 struct ath_softc
*sc
= file
->private_data
;
51 struct ath_hw
*ah
= sc
->sc_ah
;
54 u32 val
[ATH9K_NUM_DMA_DEBUG_REGS
];
55 int i
, qcuOffset
= 0, dcuOffset
= 0;
56 u32
*qcuBase
= &val
[0], *dcuBase
= &val
[4];
58 REG_WRITE(ah
, AR_MACMISC
,
59 ((AR_MACMISC_DMA_OBS_LINE_8
<< AR_MACMISC_DMA_OBS_S
) |
60 (AR_MACMISC_MISC_OBS_BUS_1
<<
61 AR_MACMISC_MISC_OBS_BUS_MSB_S
)));
63 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
64 "Raw DMA Debug values:\n");
66 for (i
= 0; i
< ATH9K_NUM_DMA_DEBUG_REGS
; i
++) {
68 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
, "\n");
70 val
[i
] = REG_READ(ah
, AR_DMADBG_0
+ (i
* sizeof(u32
)));
71 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
, "%d: %08x ",
75 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
, "\n\n");
76 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
77 "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n");
79 for (i
= 0; i
< ATH9K_NUM_QUEUES
; i
++, qcuOffset
+= 4, dcuOffset
+= 5) {
90 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
91 "%2d %2x %1x %2x %2x\n",
92 i
, (*qcuBase
& (0x7 << qcuOffset
)) >> qcuOffset
,
93 (*qcuBase
& (0x8 << qcuOffset
)) >> (qcuOffset
+ 3),
94 val
[2] & (0x7 << (i
* 3)) >> (i
* 3),
95 (*dcuBase
& (0x1f << dcuOffset
)) >> dcuOffset
);
98 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
, "\n");
100 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
101 "qcu_stitch state: %2x qcu_fetch state: %2x\n",
102 (val
[3] & 0x003c0000) >> 18, (val
[3] & 0x03c00000) >> 22);
103 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
104 "qcu_complete state: %2x dcu_complete state: %2x\n",
105 (val
[3] & 0x1c000000) >> 26, (val
[6] & 0x3));
106 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
107 "dcu_arb state: %2x dcu_fp state: %2x\n",
108 (val
[5] & 0x06000000) >> 25, (val
[5] & 0x38000000) >> 27);
109 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
110 "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n",
111 (val
[6] & 0x000003fc) >> 2, (val
[6] & 0x00000400) >> 10);
112 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
113 "txfifo_valid_0: %1d txfifo_valid_1: %1d\n",
114 (val
[6] & 0x00000800) >> 11, (val
[6] & 0x00001000) >> 12);
115 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
116 "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n",
117 (val
[6] & 0x0001e000) >> 13, (val
[6] & 0x001e0000) >> 17);
119 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
, "pcu observe: 0x%x \n",
120 REG_READ(ah
, AR_OBS_BUS_1
));
121 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
122 "AR_CR: 0x%x \n", REG_READ(ah
, AR_CR
));
124 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
127 static const struct file_operations fops_dma
= {
128 .read
= read_file_dma
,
129 .open
= ath9k_debugfs_open
,
134 void ath_debug_stat_interrupt(struct ath_softc
*sc
, enum ath9k_int status
)
137 sc
->debug
.stats
.istats
.total
++;
138 if (status
& ATH9K_INT_RX
)
139 sc
->debug
.stats
.istats
.rxok
++;
140 if (status
& ATH9K_INT_RXEOL
)
141 sc
->debug
.stats
.istats
.rxeol
++;
142 if (status
& ATH9K_INT_RXORN
)
143 sc
->debug
.stats
.istats
.rxorn
++;
144 if (status
& ATH9K_INT_TX
)
145 sc
->debug
.stats
.istats
.txok
++;
146 if (status
& ATH9K_INT_TXURN
)
147 sc
->debug
.stats
.istats
.txurn
++;
148 if (status
& ATH9K_INT_MIB
)
149 sc
->debug
.stats
.istats
.mib
++;
150 if (status
& ATH9K_INT_RXPHY
)
151 sc
->debug
.stats
.istats
.rxphyerr
++;
152 if (status
& ATH9K_INT_RXKCM
)
153 sc
->debug
.stats
.istats
.rx_keycache_miss
++;
154 if (status
& ATH9K_INT_SWBA
)
155 sc
->debug
.stats
.istats
.swba
++;
156 if (status
& ATH9K_INT_BMISS
)
157 sc
->debug
.stats
.istats
.bmiss
++;
158 if (status
& ATH9K_INT_BNR
)
159 sc
->debug
.stats
.istats
.bnr
++;
160 if (status
& ATH9K_INT_CST
)
161 sc
->debug
.stats
.istats
.cst
++;
162 if (status
& ATH9K_INT_GTT
)
163 sc
->debug
.stats
.istats
.gtt
++;
164 if (status
& ATH9K_INT_TIM
)
165 sc
->debug
.stats
.istats
.tim
++;
166 if (status
& ATH9K_INT_CABEND
)
167 sc
->debug
.stats
.istats
.cabend
++;
168 if (status
& ATH9K_INT_DTIMSYNC
)
169 sc
->debug
.stats
.istats
.dtimsync
++;
170 if (status
& ATH9K_INT_DTIM
)
171 sc
->debug
.stats
.istats
.dtim
++;
174 static ssize_t
read_file_interrupt(struct file
*file
, char __user
*user_buf
,
175 size_t count
, loff_t
*ppos
)
177 struct ath_softc
*sc
= file
->private_data
;
179 unsigned int len
= 0;
181 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
182 "%8s: %10u\n", "RX", sc
->debug
.stats
.istats
.rxok
);
183 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
184 "%8s: %10u\n", "RXEOL", sc
->debug
.stats
.istats
.rxeol
);
185 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
186 "%8s: %10u\n", "RXORN", sc
->debug
.stats
.istats
.rxorn
);
187 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
188 "%8s: %10u\n", "TX", sc
->debug
.stats
.istats
.txok
);
189 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
190 "%8s: %10u\n", "TXURN", sc
->debug
.stats
.istats
.txurn
);
191 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
192 "%8s: %10u\n", "MIB", sc
->debug
.stats
.istats
.mib
);
193 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
194 "%8s: %10u\n", "RXPHY", sc
->debug
.stats
.istats
.rxphyerr
);
195 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
196 "%8s: %10u\n", "RXKCM", sc
->debug
.stats
.istats
.rx_keycache_miss
);
197 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
198 "%8s: %10u\n", "SWBA", sc
->debug
.stats
.istats
.swba
);
199 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
200 "%8s: %10u\n", "BMISS", sc
->debug
.stats
.istats
.bmiss
);
201 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
202 "%8s: %10u\n", "BNR", sc
->debug
.stats
.istats
.bnr
);
203 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
204 "%8s: %10u\n", "CST", sc
->debug
.stats
.istats
.cst
);
205 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
206 "%8s: %10u\n", "GTT", sc
->debug
.stats
.istats
.gtt
);
207 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
208 "%8s: %10u\n", "TIM", sc
->debug
.stats
.istats
.tim
);
209 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
210 "%8s: %10u\n", "CABEND", sc
->debug
.stats
.istats
.cabend
);
211 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
212 "%8s: %10u\n", "DTIMSYNC", sc
->debug
.stats
.istats
.dtimsync
);
213 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
214 "%8s: %10u\n", "DTIM", sc
->debug
.stats
.istats
.dtim
);
215 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
216 "%8s: %10u\n", "TOTAL", sc
->debug
.stats
.istats
.total
);
218 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
221 static const struct file_operations fops_interrupt
= {
222 .read
= read_file_interrupt
,
223 .open
= ath9k_debugfs_open
,
227 void ath_debug_stat_rc(struct ath_softc
*sc
, struct sk_buff
*skb
)
229 struct ath_tx_info_priv
*tx_info_priv
= NULL
;
230 struct ieee80211_tx_info
*tx_info
= IEEE80211_SKB_CB(skb
);
231 struct ieee80211_tx_rate
*rates
= tx_info
->status
.rates
;
232 int final_ts_idx
, idx
;
233 struct ath_rc_stats
*stats
;
235 tx_info_priv
= ATH_TX_INFO_PRIV(tx_info
);
236 final_ts_idx
= tx_info_priv
->tx
.ts_rateindex
;
237 idx
= rates
[final_ts_idx
].idx
;
238 stats
= &sc
->debug
.stats
.rcstats
[idx
];
242 void ath_debug_stat_retries(struct ath_softc
*sc
, int rix
,
243 int xretries
, int retries
, u8 per
)
245 struct ath_rc_stats
*stats
= &sc
->debug
.stats
.rcstats
[rix
];
247 stats
->xretries
+= xretries
;
248 stats
->retries
+= retries
;
252 static ssize_t
read_file_rcstat(struct file
*file
, char __user
*user_buf
,
253 size_t count
, loff_t
*ppos
)
255 struct ath_softc
*sc
= file
->private_data
;
257 unsigned int len
= 0, max
;
261 if (sc
->cur_rate_table
== NULL
)
264 max
= 80 + sc
->cur_rate_table
->rate_cnt
* 64;
265 buf
= kmalloc(max
+ 1, GFP_KERNEL
);
270 len
+= sprintf(buf
, "%5s %15s %8s %9s %3s\n\n", "Rate", "Success",
271 "Retries", "XRetries", "PER");
273 for (i
= 0; i
< sc
->cur_rate_table
->rate_cnt
; i
++) {
274 u32 ratekbps
= sc
->cur_rate_table
->info
[i
].ratekbps
;
275 struct ath_rc_stats
*stats
= &sc
->debug
.stats
.rcstats
[i
];
277 len
+= snprintf(buf
+ len
, max
- len
,
278 "%3u.%d: %8u %8u %8u %8u\n", ratekbps
/ 1000,
279 (ratekbps
% 1000) / 100, stats
->success
,
280 stats
->retries
, stats
->xretries
,
284 retval
= simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
289 static const struct file_operations fops_rcstat
= {
290 .read
= read_file_rcstat
,
291 .open
= ath9k_debugfs_open
,
295 static const char * ath_wiphy_state_str(enum ath_wiphy_state state
)
298 case ATH_WIPHY_INACTIVE
:
300 case ATH_WIPHY_ACTIVE
:
302 case ATH_WIPHY_PAUSING
:
304 case ATH_WIPHY_PAUSED
:
312 static ssize_t
read_file_wiphy(struct file
*file
, char __user
*user_buf
,
313 size_t count
, loff_t
*ppos
)
315 struct ath_softc
*sc
= file
->private_data
;
317 unsigned int len
= 0;
321 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
322 "primary: %s (%s chan=%d ht=%d)\n",
323 wiphy_name(sc
->pri_wiphy
->hw
->wiphy
),
324 ath_wiphy_state_str(sc
->pri_wiphy
->state
),
325 sc
->pri_wiphy
->chan_idx
, sc
->pri_wiphy
->chan_is_ht
);
326 for (i
= 0; i
< sc
->num_sec_wiphy
; i
++) {
327 struct ath_wiphy
*aphy
= sc
->sec_wiphy
[i
];
330 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
331 "secondary: %s (%s chan=%d ht=%d)\n",
332 wiphy_name(aphy
->hw
->wiphy
),
333 ath_wiphy_state_str(aphy
->state
),
334 aphy
->chan_idx
, aphy
->chan_is_ht
);
337 put_unaligned_le32(REG_READ(sc
->sc_ah
, AR_STA_ID0
), addr
);
338 put_unaligned_le16(REG_READ(sc
->sc_ah
, AR_STA_ID1
) & 0xffff, addr
+ 4);
339 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
340 "addr: %pM\n", addr
);
341 put_unaligned_le32(REG_READ(sc
->sc_ah
, AR_BSSMSKL
), addr
);
342 put_unaligned_le16(REG_READ(sc
->sc_ah
, AR_BSSMSKU
) & 0xffff, addr
+ 4);
343 len
+= snprintf(buf
+ len
, sizeof(buf
) - len
,
344 "addrmask: %pM\n", addr
);
346 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
349 static struct ath_wiphy
* get_wiphy(struct ath_softc
*sc
, const char *name
)
352 if (strcmp(name
, wiphy_name(sc
->pri_wiphy
->hw
->wiphy
)) == 0)
353 return sc
->pri_wiphy
;
354 for (i
= 0; i
< sc
->num_sec_wiphy
; i
++) {
355 struct ath_wiphy
*aphy
= sc
->sec_wiphy
[i
];
356 if (aphy
&& strcmp(name
, wiphy_name(aphy
->hw
->wiphy
)) == 0)
362 static int del_wiphy(struct ath_softc
*sc
, const char *name
)
364 struct ath_wiphy
*aphy
= get_wiphy(sc
, name
);
367 return ath9k_wiphy_del(aphy
);
370 static int pause_wiphy(struct ath_softc
*sc
, const char *name
)
372 struct ath_wiphy
*aphy
= get_wiphy(sc
, name
);
375 return ath9k_wiphy_pause(aphy
);
378 static int unpause_wiphy(struct ath_softc
*sc
, const char *name
)
380 struct ath_wiphy
*aphy
= get_wiphy(sc
, name
);
383 return ath9k_wiphy_unpause(aphy
);
386 static int select_wiphy(struct ath_softc
*sc
, const char *name
)
388 struct ath_wiphy
*aphy
= get_wiphy(sc
, name
);
391 return ath9k_wiphy_select(aphy
);
394 static int schedule_wiphy(struct ath_softc
*sc
, const char *msec
)
396 ath9k_wiphy_set_scheduler(sc
, simple_strtoul(msec
, NULL
, 0));
400 static ssize_t
write_file_wiphy(struct file
*file
, const char __user
*user_buf
,
401 size_t count
, loff_t
*ppos
)
403 struct ath_softc
*sc
= file
->private_data
;
407 len
= min(count
, sizeof(buf
) - 1);
408 if (copy_from_user(buf
, user_buf
, len
))
411 if (len
> 0 && buf
[len
- 1] == '\n')
414 if (strncmp(buf
, "add", 3) == 0) {
415 int res
= ath9k_wiphy_add(sc
);
418 } else if (strncmp(buf
, "del=", 4) == 0) {
419 int res
= del_wiphy(sc
, buf
+ 4);
422 } else if (strncmp(buf
, "pause=", 6) == 0) {
423 int res
= pause_wiphy(sc
, buf
+ 6);
426 } else if (strncmp(buf
, "unpause=", 8) == 0) {
427 int res
= unpause_wiphy(sc
, buf
+ 8);
430 } else if (strncmp(buf
, "select=", 7) == 0) {
431 int res
= select_wiphy(sc
, buf
+ 7);
434 } else if (strncmp(buf
, "schedule=", 9) == 0) {
435 int res
= schedule_wiphy(sc
, buf
+ 9);
444 static const struct file_operations fops_wiphy
= {
445 .read
= read_file_wiphy
,
446 .write
= write_file_wiphy
,
447 .open
= ath9k_debugfs_open
,
452 int ath9k_init_debug(struct ath_softc
*sc
)
454 sc
->debug
.debug_mask
= ath9k_debug
;
456 if (!ath9k_debugfs_root
)
459 sc
->debug
.debugfs_phy
= debugfs_create_dir(wiphy_name(sc
->hw
->wiphy
),
461 if (!sc
->debug
.debugfs_phy
)
464 sc
->debug
.debugfs_dma
= debugfs_create_file("dma", S_IRUGO
,
465 sc
->debug
.debugfs_phy
, sc
, &fops_dma
);
466 if (!sc
->debug
.debugfs_dma
)
469 sc
->debug
.debugfs_interrupt
= debugfs_create_file("interrupt",
471 sc
->debug
.debugfs_phy
,
472 sc
, &fops_interrupt
);
473 if (!sc
->debug
.debugfs_interrupt
)
476 sc
->debug
.debugfs_rcstat
= debugfs_create_file("rcstat",
478 sc
->debug
.debugfs_phy
,
480 if (!sc
->debug
.debugfs_rcstat
)
483 sc
->debug
.debugfs_wiphy
= debugfs_create_file(
484 "wiphy", S_IRUGO
| S_IWUSR
, sc
->debug
.debugfs_phy
, sc
,
486 if (!sc
->debug
.debugfs_wiphy
)
491 ath9k_exit_debug(sc
);
495 void ath9k_exit_debug(struct ath_softc
*sc
)
497 debugfs_remove(sc
->debug
.debugfs_wiphy
);
498 debugfs_remove(sc
->debug
.debugfs_rcstat
);
499 debugfs_remove(sc
->debug
.debugfs_interrupt
);
500 debugfs_remove(sc
->debug
.debugfs_dma
);
501 debugfs_remove(sc
->debug
.debugfs_phy
);
504 int ath9k_debug_create_root(void)
506 ath9k_debugfs_root
= debugfs_create_dir(KBUILD_MODNAME
, NULL
);
507 if (!ath9k_debugfs_root
)
513 void ath9k_debug_remove_root(void)
515 debugfs_remove(ath9k_debugfs_root
);
516 ath9k_debugfs_root
= NULL
;