diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/debug.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/debug.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c new file mode 100644 index 000000000000..6d20725d6451 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/debug.c | |||
@@ -0,0 +1,561 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008-2009 Atheros Communications 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 <asm/unaligned.h> | ||
18 | |||
19 | #include "ath9k.h" | ||
20 | |||
21 | static unsigned int ath9k_debug = DBG_DEFAULT; | ||
22 | module_param_named(debug, ath9k_debug, uint, 0); | ||
23 | |||
24 | static struct dentry *ath9k_debugfs_root; | ||
25 | |||
26 | void DPRINTF(struct ath_softc *sc, int dbg_mask, const char *fmt, ...) | ||
27 | { | ||
28 | if (!sc) | ||
29 | return; | ||
30 | |||
31 | if (sc->debug.debug_mask & dbg_mask) { | ||
32 | va_list args; | ||
33 | |||
34 | va_start(args, fmt); | ||
35 | printk(KERN_DEBUG "ath9k: "); | ||
36 | vprintk(fmt, args); | ||
37 | va_end(args); | ||
38 | } | ||
39 | } | ||
40 | |||
41 | static int ath9k_debugfs_open(struct inode *inode, struct file *file) | ||
42 | { | ||
43 | file->private_data = inode->i_private; | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static ssize_t read_file_debug(struct file *file, char __user *user_buf, | ||
48 | size_t count, loff_t *ppos) | ||
49 | { | ||
50 | struct ath_softc *sc = file->private_data; | ||
51 | char buf[32]; | ||
52 | unsigned int len; | ||
53 | |||
54 | len = snprintf(buf, sizeof(buf), "0x%08x\n", sc->debug.debug_mask); | ||
55 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
56 | } | ||
57 | |||
58 | static ssize_t write_file_debug(struct file *file, const char __user *user_buf, | ||
59 | size_t count, loff_t *ppos) | ||
60 | { | ||
61 | struct ath_softc *sc = file->private_data; | ||
62 | unsigned long mask; | ||
63 | char buf[32]; | ||
64 | ssize_t len; | ||
65 | |||
66 | len = min(count, sizeof(buf) - 1); | ||
67 | if (copy_from_user(buf, user_buf, len)) | ||
68 | return -EINVAL; | ||
69 | |||
70 | buf[len] = '\0'; | ||
71 | if (strict_strtoul(buf, 0, &mask)) | ||
72 | return -EINVAL; | ||
73 | |||
74 | sc->debug.debug_mask = mask; | ||
75 | return count; | ||
76 | } | ||
77 | |||
78 | static const struct file_operations fops_debug = { | ||
79 | .read = read_file_debug, | ||
80 | .write = write_file_debug, | ||
81 | .open = ath9k_debugfs_open, | ||
82 | .owner = THIS_MODULE | ||
83 | }; | ||
84 | |||
85 | static ssize_t read_file_dma(struct file *file, char __user *user_buf, | ||
86 | size_t count, loff_t *ppos) | ||
87 | { | ||
88 | struct ath_softc *sc = file->private_data; | ||
89 | struct ath_hw *ah = sc->sc_ah; | ||
90 | char buf[1024]; | ||
91 | unsigned int len = 0; | ||
92 | u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; | ||
93 | int i, qcuOffset = 0, dcuOffset = 0; | ||
94 | u32 *qcuBase = &val[0], *dcuBase = &val[4]; | ||
95 | |||
96 | REG_WRITE(ah, AR_MACMISC, | ||
97 | ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | | ||
98 | (AR_MACMISC_MISC_OBS_BUS_1 << | ||
99 | AR_MACMISC_MISC_OBS_BUS_MSB_S))); | ||
100 | |||
101 | len += snprintf(buf + len, sizeof(buf) - len, | ||
102 | "Raw DMA Debug values:\n"); | ||
103 | |||
104 | for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { | ||
105 | if (i % 4 == 0) | ||
106 | len += snprintf(buf + len, sizeof(buf) - len, "\n"); | ||
107 | |||
108 | val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32))); | ||
109 | len += snprintf(buf + len, sizeof(buf) - len, "%d: %08x ", | ||
110 | i, val[i]); | ||
111 | } | ||
112 | |||
113 | len += snprintf(buf + len, sizeof(buf) - len, "\n\n"); | ||
114 | len += snprintf(buf + len, sizeof(buf) - len, | ||
115 | "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); | ||
116 | |||
117 | for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { | ||
118 | if (i == 8) { | ||
119 | qcuOffset = 0; | ||
120 | qcuBase++; | ||
121 | } | ||
122 | |||
123 | if (i == 6) { | ||
124 | dcuOffset = 0; | ||
125 | dcuBase++; | ||
126 | } | ||
127 | |||
128 | len += snprintf(buf + len, sizeof(buf) - len, | ||
129 | "%2d %2x %1x %2x %2x\n", | ||
130 | i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, | ||
131 | (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), | ||
132 | val[2] & (0x7 << (i * 3)) >> (i * 3), | ||
133 | (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); | ||
134 | } | ||
135 | |||
136 | len += snprintf(buf + len, sizeof(buf) - len, "\n"); | ||
137 | |||
138 | len += snprintf(buf + len, sizeof(buf) - len, | ||
139 | "qcu_stitch state: %2x qcu_fetch state: %2x\n", | ||
140 | (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); | ||
141 | len += snprintf(buf + len, sizeof(buf) - len, | ||
142 | "qcu_complete state: %2x dcu_complete state: %2x\n", | ||
143 | (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); | ||
144 | len += snprintf(buf + len, sizeof(buf) - len, | ||
145 | "dcu_arb state: %2x dcu_fp state: %2x\n", | ||
146 | (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); | ||
147 | len += snprintf(buf + len, sizeof(buf) - len, | ||
148 | "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", | ||
149 | (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); | ||
150 | len += snprintf(buf + len, sizeof(buf) - len, | ||
151 | "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", | ||
152 | (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); | ||
153 | len += snprintf(buf + len, sizeof(buf) - len, | ||
154 | "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", | ||
155 | (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); | ||
156 | |||
157 | len += snprintf(buf + len, sizeof(buf) - len, "pcu observe: 0x%x \n", | ||
158 | REG_READ(ah, AR_OBS_BUS_1)); | ||
159 | len += snprintf(buf + len, sizeof(buf) - len, | ||
160 | "AR_CR: 0x%x \n", REG_READ(ah, AR_CR)); | ||
161 | |||
162 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
163 | } | ||
164 | |||
165 | static const struct file_operations fops_dma = { | ||
166 | .read = read_file_dma, | ||
167 | .open = ath9k_debugfs_open, | ||
168 | .owner = THIS_MODULE | ||
169 | }; | ||
170 | |||
171 | |||
172 | void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) | ||
173 | { | ||
174 | if (status) | ||
175 | sc->debug.stats.istats.total++; | ||
176 | if (status & ATH9K_INT_RX) | ||
177 | sc->debug.stats.istats.rxok++; | ||
178 | if (status & ATH9K_INT_RXEOL) | ||
179 | sc->debug.stats.istats.rxeol++; | ||
180 | if (status & ATH9K_INT_RXORN) | ||
181 | sc->debug.stats.istats.rxorn++; | ||
182 | if (status & ATH9K_INT_TX) | ||
183 | sc->debug.stats.istats.txok++; | ||
184 | if (status & ATH9K_INT_TXURN) | ||
185 | sc->debug.stats.istats.txurn++; | ||
186 | if (status & ATH9K_INT_MIB) | ||
187 | sc->debug.stats.istats.mib++; | ||
188 | if (status & ATH9K_INT_RXPHY) | ||
189 | sc->debug.stats.istats.rxphyerr++; | ||
190 | if (status & ATH9K_INT_RXKCM) | ||
191 | sc->debug.stats.istats.rx_keycache_miss++; | ||
192 | if (status & ATH9K_INT_SWBA) | ||
193 | sc->debug.stats.istats.swba++; | ||
194 | if (status & ATH9K_INT_BMISS) | ||
195 | sc->debug.stats.istats.bmiss++; | ||
196 | if (status & ATH9K_INT_BNR) | ||
197 | sc->debug.stats.istats.bnr++; | ||
198 | if (status & ATH9K_INT_CST) | ||
199 | sc->debug.stats.istats.cst++; | ||
200 | if (status & ATH9K_INT_GTT) | ||
201 | sc->debug.stats.istats.gtt++; | ||
202 | if (status & ATH9K_INT_TIM) | ||
203 | sc->debug.stats.istats.tim++; | ||
204 | if (status & ATH9K_INT_CABEND) | ||
205 | sc->debug.stats.istats.cabend++; | ||
206 | if (status & ATH9K_INT_DTIMSYNC) | ||
207 | sc->debug.stats.istats.dtimsync++; | ||
208 | if (status & ATH9K_INT_DTIM) | ||
209 | sc->debug.stats.istats.dtim++; | ||
210 | } | ||
211 | |||
212 | static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, | ||
213 | size_t count, loff_t *ppos) | ||
214 | { | ||
215 | struct ath_softc *sc = file->private_data; | ||
216 | char buf[512]; | ||
217 | unsigned int len = 0; | ||
218 | |||
219 | len += snprintf(buf + len, sizeof(buf) - len, | ||
220 | "%8s: %10u\n", "RX", sc->debug.stats.istats.rxok); | ||
221 | len += snprintf(buf + len, sizeof(buf) - len, | ||
222 | "%8s: %10u\n", "RXEOL", sc->debug.stats.istats.rxeol); | ||
223 | len += snprintf(buf + len, sizeof(buf) - len, | ||
224 | "%8s: %10u\n", "RXORN", sc->debug.stats.istats.rxorn); | ||
225 | len += snprintf(buf + len, sizeof(buf) - len, | ||
226 | "%8s: %10u\n", "TX", sc->debug.stats.istats.txok); | ||
227 | len += snprintf(buf + len, sizeof(buf) - len, | ||
228 | "%8s: %10u\n", "TXURN", sc->debug.stats.istats.txurn); | ||
229 | len += snprintf(buf + len, sizeof(buf) - len, | ||
230 | "%8s: %10u\n", "MIB", sc->debug.stats.istats.mib); | ||
231 | len += snprintf(buf + len, sizeof(buf) - len, | ||
232 | "%8s: %10u\n", "RXPHY", sc->debug.stats.istats.rxphyerr); | ||
233 | len += snprintf(buf + len, sizeof(buf) - len, | ||
234 | "%8s: %10u\n", "RXKCM", sc->debug.stats.istats.rx_keycache_miss); | ||
235 | len += snprintf(buf + len, sizeof(buf) - len, | ||
236 | "%8s: %10u\n", "SWBA", sc->debug.stats.istats.swba); | ||
237 | len += snprintf(buf + len, sizeof(buf) - len, | ||
238 | "%8s: %10u\n", "BMISS", sc->debug.stats.istats.bmiss); | ||
239 | len += snprintf(buf + len, sizeof(buf) - len, | ||
240 | "%8s: %10u\n", "BNR", sc->debug.stats.istats.bnr); | ||
241 | len += snprintf(buf + len, sizeof(buf) - len, | ||
242 | "%8s: %10u\n", "CST", sc->debug.stats.istats.cst); | ||
243 | len += snprintf(buf + len, sizeof(buf) - len, | ||
244 | "%8s: %10u\n", "GTT", sc->debug.stats.istats.gtt); | ||
245 | len += snprintf(buf + len, sizeof(buf) - len, | ||
246 | "%8s: %10u\n", "TIM", sc->debug.stats.istats.tim); | ||
247 | len += snprintf(buf + len, sizeof(buf) - len, | ||
248 | "%8s: %10u\n", "CABEND", sc->debug.stats.istats.cabend); | ||
249 | len += snprintf(buf + len, sizeof(buf) - len, | ||
250 | "%8s: %10u\n", "DTIMSYNC", sc->debug.stats.istats.dtimsync); | ||
251 | len += snprintf(buf + len, sizeof(buf) - len, | ||
252 | "%8s: %10u\n", "DTIM", sc->debug.stats.istats.dtim); | ||
253 | len += snprintf(buf + len, sizeof(buf) - len, | ||
254 | "%8s: %10u\n", "TOTAL", sc->debug.stats.istats.total); | ||
255 | |||
256 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
257 | } | ||
258 | |||
259 | static const struct file_operations fops_interrupt = { | ||
260 | .read = read_file_interrupt, | ||
261 | .open = ath9k_debugfs_open, | ||
262 | .owner = THIS_MODULE | ||
263 | }; | ||
264 | |||
265 | void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb) | ||
266 | { | ||
267 | struct ath_tx_info_priv *tx_info_priv = NULL; | ||
268 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | ||
269 | struct ieee80211_tx_rate *rates = tx_info->status.rates; | ||
270 | int final_ts_idx, idx; | ||
271 | struct ath_rc_stats *stats; | ||
272 | |||
273 | tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | ||
274 | final_ts_idx = tx_info_priv->tx.ts_rateindex; | ||
275 | idx = rates[final_ts_idx].idx; | ||
276 | stats = &sc->debug.stats.rcstats[idx]; | ||
277 | stats->success++; | ||
278 | } | ||
279 | |||
280 | void ath_debug_stat_retries(struct ath_softc *sc, int rix, | ||
281 | int xretries, int retries, u8 per) | ||
282 | { | ||
283 | struct ath_rc_stats *stats = &sc->debug.stats.rcstats[rix]; | ||
284 | |||
285 | stats->xretries += xretries; | ||
286 | stats->retries += retries; | ||
287 | stats->per = per; | ||
288 | } | ||
289 | |||
290 | static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, | ||
291 | size_t count, loff_t *ppos) | ||
292 | { | ||
293 | struct ath_softc *sc = file->private_data; | ||
294 | char *buf; | ||
295 | unsigned int len = 0, max; | ||
296 | int i = 0; | ||
297 | ssize_t retval; | ||
298 | |||
299 | if (sc->cur_rate_table == NULL) | ||
300 | return 0; | ||
301 | |||
302 | max = 80 + sc->cur_rate_table->rate_cnt * 64; | ||
303 | buf = kmalloc(max + 1, GFP_KERNEL); | ||
304 | if (buf == NULL) | ||
305 | return 0; | ||
306 | buf[max] = 0; | ||
307 | |||
308 | len += sprintf(buf, "%5s %15s %8s %9s %3s\n\n", "Rate", "Success", | ||
309 | "Retries", "XRetries", "PER"); | ||
310 | |||
311 | for (i = 0; i < sc->cur_rate_table->rate_cnt; i++) { | ||
312 | u32 ratekbps = sc->cur_rate_table->info[i].ratekbps; | ||
313 | struct ath_rc_stats *stats = &sc->debug.stats.rcstats[i]; | ||
314 | |||
315 | len += snprintf(buf + len, max - len, | ||
316 | "%3u.%d: %8u %8u %8u %8u\n", ratekbps / 1000, | ||
317 | (ratekbps % 1000) / 100, stats->success, | ||
318 | stats->retries, stats->xretries, | ||
319 | stats->per); | ||
320 | } | ||
321 | |||
322 | retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
323 | kfree(buf); | ||
324 | return retval; | ||
325 | } | ||
326 | |||
327 | static const struct file_operations fops_rcstat = { | ||
328 | .read = read_file_rcstat, | ||
329 | .open = ath9k_debugfs_open, | ||
330 | .owner = THIS_MODULE | ||
331 | }; | ||
332 | |||
333 | static const char * ath_wiphy_state_str(enum ath_wiphy_state state) | ||
334 | { | ||
335 | switch (state) { | ||
336 | case ATH_WIPHY_INACTIVE: | ||
337 | return "INACTIVE"; | ||
338 | case ATH_WIPHY_ACTIVE: | ||
339 | return "ACTIVE"; | ||
340 | case ATH_WIPHY_PAUSING: | ||
341 | return "PAUSING"; | ||
342 | case ATH_WIPHY_PAUSED: | ||
343 | return "PAUSED"; | ||
344 | case ATH_WIPHY_SCAN: | ||
345 | return "SCAN"; | ||
346 | } | ||
347 | return "?"; | ||
348 | } | ||
349 | |||
350 | static ssize_t read_file_wiphy(struct file *file, char __user *user_buf, | ||
351 | size_t count, loff_t *ppos) | ||
352 | { | ||
353 | struct ath_softc *sc = file->private_data; | ||
354 | char buf[512]; | ||
355 | unsigned int len = 0; | ||
356 | int i; | ||
357 | u8 addr[ETH_ALEN]; | ||
358 | |||
359 | len += snprintf(buf + len, sizeof(buf) - len, | ||
360 | "primary: %s (%s chan=%d ht=%d)\n", | ||
361 | wiphy_name(sc->pri_wiphy->hw->wiphy), | ||
362 | ath_wiphy_state_str(sc->pri_wiphy->state), | ||
363 | sc->pri_wiphy->chan_idx, sc->pri_wiphy->chan_is_ht); | ||
364 | for (i = 0; i < sc->num_sec_wiphy; i++) { | ||
365 | struct ath_wiphy *aphy = sc->sec_wiphy[i]; | ||
366 | if (aphy == NULL) | ||
367 | continue; | ||
368 | len += snprintf(buf + len, sizeof(buf) - len, | ||
369 | "secondary: %s (%s chan=%d ht=%d)\n", | ||
370 | wiphy_name(aphy->hw->wiphy), | ||
371 | ath_wiphy_state_str(aphy->state), | ||
372 | aphy->chan_idx, aphy->chan_is_ht); | ||
373 | } | ||
374 | |||
375 | put_unaligned_le32(REG_READ(sc->sc_ah, AR_STA_ID0), addr); | ||
376 | put_unaligned_le16(REG_READ(sc->sc_ah, AR_STA_ID1) & 0xffff, addr + 4); | ||
377 | len += snprintf(buf + len, sizeof(buf) - len, | ||
378 | "addr: %pM\n", addr); | ||
379 | put_unaligned_le32(REG_READ(sc->sc_ah, AR_BSSMSKL), addr); | ||
380 | put_unaligned_le16(REG_READ(sc->sc_ah, AR_BSSMSKU) & 0xffff, addr + 4); | ||
381 | len += snprintf(buf + len, sizeof(buf) - len, | ||
382 | "addrmask: %pM\n", addr); | ||
383 | |||
384 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | ||
385 | } | ||
386 | |||
387 | static struct ath_wiphy * get_wiphy(struct ath_softc *sc, const char *name) | ||
388 | { | ||
389 | int i; | ||
390 | if (strcmp(name, wiphy_name(sc->pri_wiphy->hw->wiphy)) == 0) | ||
391 | return sc->pri_wiphy; | ||
392 | for (i = 0; i < sc->num_sec_wiphy; i++) { | ||
393 | struct ath_wiphy *aphy = sc->sec_wiphy[i]; | ||
394 | if (aphy && strcmp(name, wiphy_name(aphy->hw->wiphy)) == 0) | ||
395 | return aphy; | ||
396 | } | ||
397 | return NULL; | ||
398 | } | ||
399 | |||
400 | static int del_wiphy(struct ath_softc *sc, const char *name) | ||
401 | { | ||
402 | struct ath_wiphy *aphy = get_wiphy(sc, name); | ||
403 | if (!aphy) | ||
404 | return -ENOENT; | ||
405 | return ath9k_wiphy_del(aphy); | ||
406 | } | ||
407 | |||
408 | static int pause_wiphy(struct ath_softc *sc, const char *name) | ||
409 | { | ||
410 | struct ath_wiphy *aphy = get_wiphy(sc, name); | ||
411 | if (!aphy) | ||
412 | return -ENOENT; | ||
413 | return ath9k_wiphy_pause(aphy); | ||
414 | } | ||
415 | |||
416 | static int unpause_wiphy(struct ath_softc *sc, const char *name) | ||
417 | { | ||
418 | struct ath_wiphy *aphy = get_wiphy(sc, name); | ||
419 | if (!aphy) | ||
420 | return -ENOENT; | ||
421 | return ath9k_wiphy_unpause(aphy); | ||
422 | } | ||
423 | |||
424 | static int select_wiphy(struct ath_softc *sc, const char *name) | ||
425 | { | ||
426 | struct ath_wiphy *aphy = get_wiphy(sc, name); | ||
427 | if (!aphy) | ||
428 | return -ENOENT; | ||
429 | return ath9k_wiphy_select(aphy); | ||
430 | } | ||
431 | |||
432 | static int schedule_wiphy(struct ath_softc *sc, const char *msec) | ||
433 | { | ||
434 | ath9k_wiphy_set_scheduler(sc, simple_strtoul(msec, NULL, 0)); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static ssize_t write_file_wiphy(struct file *file, const char __user *user_buf, | ||
439 | size_t count, loff_t *ppos) | ||
440 | { | ||
441 | struct ath_softc *sc = file->private_data; | ||
442 | char buf[50]; | ||
443 | size_t len; | ||
444 | |||
445 | len = min(count, sizeof(buf) - 1); | ||
446 | if (copy_from_user(buf, user_buf, len)) | ||
447 | return -EFAULT; | ||
448 | buf[len] = '\0'; | ||
449 | if (len > 0 && buf[len - 1] == '\n') | ||
450 | buf[len - 1] = '\0'; | ||
451 | |||
452 | if (strncmp(buf, "add", 3) == 0) { | ||
453 | int res = ath9k_wiphy_add(sc); | ||
454 | if (res < 0) | ||
455 | return res; | ||
456 | } else if (strncmp(buf, "del=", 4) == 0) { | ||
457 | int res = del_wiphy(sc, buf + 4); | ||
458 | if (res < 0) | ||
459 | return res; | ||
460 | } else if (strncmp(buf, "pause=", 6) == 0) { | ||
461 | int res = pause_wiphy(sc, buf + 6); | ||
462 | if (res < 0) | ||
463 | return res; | ||
464 | } else if (strncmp(buf, "unpause=", 8) == 0) { | ||
465 | int res = unpause_wiphy(sc, buf + 8); | ||
466 | if (res < 0) | ||
467 | return res; | ||
468 | } else if (strncmp(buf, "select=", 7) == 0) { | ||
469 | int res = select_wiphy(sc, buf + 7); | ||
470 | if (res < 0) | ||
471 | return res; | ||
472 | } else if (strncmp(buf, "schedule=", 9) == 0) { | ||
473 | int res = schedule_wiphy(sc, buf + 9); | ||
474 | if (res < 0) | ||
475 | return res; | ||
476 | } else | ||
477 | return -EOPNOTSUPP; | ||
478 | |||
479 | return count; | ||
480 | } | ||
481 | |||
482 | static const struct file_operations fops_wiphy = { | ||
483 | .read = read_file_wiphy, | ||
484 | .write = write_file_wiphy, | ||
485 | .open = ath9k_debugfs_open, | ||
486 | .owner = THIS_MODULE | ||
487 | }; | ||
488 | |||
489 | |||
490 | int ath9k_init_debug(struct ath_softc *sc) | ||
491 | { | ||
492 | sc->debug.debug_mask = ath9k_debug; | ||
493 | |||
494 | if (!ath9k_debugfs_root) | ||
495 | return -ENOENT; | ||
496 | |||
497 | sc->debug.debugfs_phy = debugfs_create_dir(wiphy_name(sc->hw->wiphy), | ||
498 | ath9k_debugfs_root); | ||
499 | if (!sc->debug.debugfs_phy) | ||
500 | goto err; | ||
501 | |||
502 | sc->debug.debugfs_debug = debugfs_create_file("debug", | ||
503 | S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); | ||
504 | if (!sc->debug.debugfs_debug) | ||
505 | goto err; | ||
506 | |||
507 | sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUGO, | ||
508 | sc->debug.debugfs_phy, sc, &fops_dma); | ||
509 | if (!sc->debug.debugfs_dma) | ||
510 | goto err; | ||
511 | |||
512 | sc->debug.debugfs_interrupt = debugfs_create_file("interrupt", | ||
513 | S_IRUGO, | ||
514 | sc->debug.debugfs_phy, | ||
515 | sc, &fops_interrupt); | ||
516 | if (!sc->debug.debugfs_interrupt) | ||
517 | goto err; | ||
518 | |||
519 | sc->debug.debugfs_rcstat = debugfs_create_file("rcstat", | ||
520 | S_IRUGO, | ||
521 | sc->debug.debugfs_phy, | ||
522 | sc, &fops_rcstat); | ||
523 | if (!sc->debug.debugfs_rcstat) | ||
524 | goto err; | ||
525 | |||
526 | sc->debug.debugfs_wiphy = debugfs_create_file( | ||
527 | "wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, | ||
528 | &fops_wiphy); | ||
529 | if (!sc->debug.debugfs_wiphy) | ||
530 | goto err; | ||
531 | |||
532 | return 0; | ||
533 | err: | ||
534 | ath9k_exit_debug(sc); | ||
535 | return -ENOMEM; | ||
536 | } | ||
537 | |||
538 | void ath9k_exit_debug(struct ath_softc *sc) | ||
539 | { | ||
540 | debugfs_remove(sc->debug.debugfs_wiphy); | ||
541 | debugfs_remove(sc->debug.debugfs_rcstat); | ||
542 | debugfs_remove(sc->debug.debugfs_interrupt); | ||
543 | debugfs_remove(sc->debug.debugfs_dma); | ||
544 | debugfs_remove(sc->debug.debugfs_debug); | ||
545 | debugfs_remove(sc->debug.debugfs_phy); | ||
546 | } | ||
547 | |||
548 | int ath9k_debug_create_root(void) | ||
549 | { | ||
550 | ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
551 | if (!ath9k_debugfs_root) | ||
552 | return -ENOENT; | ||
553 | |||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | void ath9k_debug_remove_root(void) | ||
558 | { | ||
559 | debugfs_remove(ath9k_debugfs_root); | ||
560 | ath9k_debugfs_root = NULL; | ||
561 | } | ||