debugfs.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. /*
  2. Broadcom B43legacy wireless driver
  3. debugfs driver debugging code
  4. Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; see the file COPYING. If not, write to
  15. the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
  16. Boston, MA 02110-1301, USA.
  17. */
  18. #include <linux/fs.h>
  19. #include <linux/debugfs.h>
  20. #include <linux/slab.h>
  21. #include <linux/netdevice.h>
  22. #include <linux/pci.h>
  23. #include <linux/mutex.h>
  24. #include "b43legacy.h"
  25. #include "main.h"
  26. #include "debugfs.h"
  27. #include "dma.h"
  28. #include "pio.h"
  29. #include "xmit.h"
  30. /* The root directory. */
  31. static struct dentry *rootdir;
  32. struct b43legacy_debugfs_fops {
  33. ssize_t (*read)(struct b43legacy_wldev *dev, char *buf, size_t bufsize);
  34. int (*write)(struct b43legacy_wldev *dev, const char *buf, size_t count);
  35. struct file_operations fops;
  36. /* Offset of struct b43legacy_dfs_file in struct b43legacy_dfsentry */
  37. size_t file_struct_offset;
  38. /* Take wl->irq_lock before calling read/write? */
  39. bool take_irqlock;
  40. };
  41. static inline
  42. struct b43legacy_dfs_file * fops_to_dfs_file(struct b43legacy_wldev *dev,
  43. const struct b43legacy_debugfs_fops *dfops)
  44. {
  45. void *p;
  46. p = dev->dfsentry;
  47. p += dfops->file_struct_offset;
  48. return p;
  49. }
  50. #define fappend(fmt, x...) \
  51. do { \
  52. if (bufsize - count) \
  53. count += snprintf(buf + count, \
  54. bufsize - count, \
  55. fmt , ##x); \
  56. else \
  57. printk(KERN_ERR "b43legacy: fappend overflow\n"); \
  58. } while (0)
  59. /* wl->irq_lock is locked */
  60. static ssize_t tsf_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
  61. {
  62. ssize_t count = 0;
  63. u64 tsf;
  64. b43legacy_tsf_read(dev, &tsf);
  65. fappend("0x%08x%08x\n",
  66. (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
  67. (unsigned int)(tsf & 0xFFFFFFFFULL));
  68. return count;
  69. }
  70. /* wl->irq_lock is locked */
  71. static int tsf_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
  72. {
  73. u64 tsf;
  74. if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1)
  75. return -EINVAL;
  76. b43legacy_tsf_write(dev, tsf);
  77. return 0;
  78. }
  79. /* wl->irq_lock is locked */
  80. static ssize_t ucode_regs_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
  81. {
  82. ssize_t count = 0;
  83. int i;
  84. for (i = 0; i < 64; i++) {
  85. fappend("r%d = 0x%04x\n", i,
  86. b43legacy_shm_read16(dev, B43legacy_SHM_WIRELESS, i));
  87. }
  88. return count;
  89. }
  90. /* wl->irq_lock is locked */
  91. static ssize_t shm_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
  92. {
  93. ssize_t count = 0;
  94. int i;
  95. u16 tmp;
  96. __le16 *le16buf = (__le16 *)buf;
  97. for (i = 0; i < 0x1000; i++) {
  98. if (bufsize < sizeof(tmp))
  99. break;
  100. tmp = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, 2 * i);
  101. le16buf[i] = cpu_to_le16(tmp);
  102. count += sizeof(tmp);
  103. bufsize -= sizeof(tmp);
  104. }
  105. return count;
  106. }
  107. static ssize_t txstat_read_file(struct b43legacy_wldev *dev, char *buf, size_t bufsize)
  108. {
  109. struct b43legacy_txstatus_log *log = &dev->dfsentry->txstatlog;
  110. ssize_t count = 0;
  111. unsigned long flags;
  112. int i, idx;
  113. struct b43legacy_txstatus *stat;
  114. spin_lock_irqsave(&log->lock, flags);
  115. if (log->end < 0) {
  116. fappend("Nothing transmitted, yet\n");
  117. goto out_unlock;
  118. }
  119. fappend("b43legacy TX status reports:\n\n"
  120. "index | cookie | seq | phy_stat | frame_count | "
  121. "rts_count | supp_reason | pm_indicated | "
  122. "intermediate | for_ampdu | acked\n" "---\n");
  123. i = log->end + 1;
  124. idx = 0;
  125. while (1) {
  126. if (i == B43legacy_NR_LOGGED_TXSTATUS)
  127. i = 0;
  128. stat = &(log->log[i]);
  129. if (stat->cookie) {
  130. fappend("%03d | "
  131. "0x%04X | 0x%04X | 0x%02X | "
  132. "0x%X | 0x%X | "
  133. "%u | %u | "
  134. "%u | %u | %u\n",
  135. idx,
  136. stat->cookie, stat->seq, stat->phy_stat,
  137. stat->frame_count, stat->rts_count,
  138. stat->supp_reason, stat->pm_indicated,
  139. stat->intermediate, stat->for_ampdu,
  140. stat->acked);
  141. idx++;
  142. }
  143. if (i == log->end)
  144. break;
  145. i++;
  146. }
  147. out_unlock:
  148. spin_unlock_irqrestore(&log->lock, flags);
  149. return count;
  150. }
  151. /* wl->irq_lock is locked */
  152. static int restart_write_file(struct b43legacy_wldev *dev, const char *buf, size_t count)
  153. {
  154. int err = 0;
  155. if (count > 0 && buf[0] == '1') {
  156. b43legacy_controller_restart(dev, "manually restarted");
  157. } else
  158. err = -EINVAL;
  159. return err;
  160. }
  161. #undef fappend
  162. static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
  163. size_t count, loff_t *ppos)
  164. {
  165. struct b43legacy_wldev *dev;
  166. struct b43legacy_debugfs_fops *dfops;
  167. struct b43legacy_dfs_file *dfile;
  168. ssize_t uninitialized_var(ret);
  169. char *buf;
  170. const size_t bufsize = 1024 * 16; /* 16 KiB buffer */
  171. const size_t buforder = get_order(bufsize);
  172. int err = 0;
  173. if (!count)
  174. return 0;
  175. dev = file->private_data;
  176. if (!dev)
  177. return -ENODEV;
  178. mutex_lock(&dev->wl->mutex);
  179. if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
  180. err = -ENODEV;
  181. goto out_unlock;
  182. }
  183. dfops = container_of(debugfs_real_fops(file),
  184. struct b43legacy_debugfs_fops, fops);
  185. if (!dfops->read) {
  186. err = -ENOSYS;
  187. goto out_unlock;
  188. }
  189. dfile = fops_to_dfs_file(dev, dfops);
  190. if (!dfile->buffer) {
  191. buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
  192. if (!buf) {
  193. err = -ENOMEM;
  194. goto out_unlock;
  195. }
  196. memset(buf, 0, bufsize);
  197. if (dfops->take_irqlock) {
  198. spin_lock_irq(&dev->wl->irq_lock);
  199. ret = dfops->read(dev, buf, bufsize);
  200. spin_unlock_irq(&dev->wl->irq_lock);
  201. } else
  202. ret = dfops->read(dev, buf, bufsize);
  203. if (ret <= 0) {
  204. free_pages((unsigned long)buf, buforder);
  205. err = ret;
  206. goto out_unlock;
  207. }
  208. dfile->data_len = ret;
  209. dfile->buffer = buf;
  210. }
  211. ret = simple_read_from_buffer(userbuf, count, ppos,
  212. dfile->buffer,
  213. dfile->data_len);
  214. if (*ppos >= dfile->data_len) {
  215. free_pages((unsigned long)dfile->buffer, buforder);
  216. dfile->buffer = NULL;
  217. dfile->data_len = 0;
  218. }
  219. out_unlock:
  220. mutex_unlock(&dev->wl->mutex);
  221. return err ? err : ret;
  222. }
  223. static ssize_t b43legacy_debugfs_write(struct file *file,
  224. const char __user *userbuf,
  225. size_t count, loff_t *ppos)
  226. {
  227. struct b43legacy_wldev *dev;
  228. struct b43legacy_debugfs_fops *dfops;
  229. char *buf;
  230. int err = 0;
  231. if (!count)
  232. return 0;
  233. if (count > PAGE_SIZE)
  234. return -E2BIG;
  235. dev = file->private_data;
  236. if (!dev)
  237. return -ENODEV;
  238. mutex_lock(&dev->wl->mutex);
  239. if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
  240. err = -ENODEV;
  241. goto out_unlock;
  242. }
  243. dfops = container_of(debugfs_real_fops(file),
  244. struct b43legacy_debugfs_fops, fops);
  245. if (!dfops->write) {
  246. err = -ENOSYS;
  247. goto out_unlock;
  248. }
  249. buf = (char *)get_zeroed_page(GFP_KERNEL);
  250. if (!buf) {
  251. err = -ENOMEM;
  252. goto out_unlock;
  253. }
  254. if (copy_from_user(buf, userbuf, count)) {
  255. err = -EFAULT;
  256. goto out_freepage;
  257. }
  258. if (dfops->take_irqlock) {
  259. spin_lock_irq(&dev->wl->irq_lock);
  260. err = dfops->write(dev, buf, count);
  261. spin_unlock_irq(&dev->wl->irq_lock);
  262. } else
  263. err = dfops->write(dev, buf, count);
  264. if (err)
  265. goto out_freepage;
  266. out_freepage:
  267. free_page((unsigned long)buf);
  268. out_unlock:
  269. mutex_unlock(&dev->wl->mutex);
  270. return err ? err : count;
  271. }
  272. #define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \
  273. static struct b43legacy_debugfs_fops fops_##name = { \
  274. .read = _read, \
  275. .write = _write, \
  276. .fops = { \
  277. .open = simple_open, \
  278. .read = b43legacy_debugfs_read, \
  279. .write = b43legacy_debugfs_write, \
  280. .llseek = generic_file_llseek, \
  281. }, \
  282. .file_struct_offset = offsetof(struct b43legacy_dfsentry, \
  283. file_##name), \
  284. .take_irqlock = _take_irqlock, \
  285. }
  286. B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);
  287. B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);
  288. B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);
  289. B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
  290. B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
  291. int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature)
  292. {
  293. return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
  294. }
  295. static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev)
  296. {
  297. struct b43legacy_dfsentry *e = dev->dfsentry;
  298. int i;
  299. for (i = 0; i < __B43legacy_NR_DYNDBG; i++)
  300. debugfs_remove(e->dyn_debug_dentries[i]);
  301. }
  302. static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)
  303. {
  304. struct b43legacy_dfsentry *e = dev->dfsentry;
  305. struct dentry *d;
  306. #define add_dyn_dbg(name, id, initstate) do { \
  307. e->dyn_debug[id] = (initstate); \
  308. d = debugfs_create_bool(name, 0600, e->subdir, \
  309. &(e->dyn_debug[id])); \
  310. if (!IS_ERR(d)) \
  311. e->dyn_debug_dentries[id] = d; \
  312. } while (0)
  313. add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, false);
  314. add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, false);
  315. add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, false);
  316. add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, false);
  317. add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, false);
  318. #undef add_dyn_dbg
  319. }
  320. void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)
  321. {
  322. struct b43legacy_dfsentry *e;
  323. struct b43legacy_txstatus_log *log;
  324. char devdir[16];
  325. B43legacy_WARN_ON(!dev);
  326. e = kzalloc(sizeof(*e), GFP_KERNEL);
  327. if (!e) {
  328. b43legacyerr(dev->wl, "debugfs: add device OOM\n");
  329. return;
  330. }
  331. e->dev = dev;
  332. log = &e->txstatlog;
  333. log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS,
  334. sizeof(struct b43legacy_txstatus), GFP_KERNEL);
  335. if (!log->log) {
  336. b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n");
  337. kfree(e);
  338. return;
  339. }
  340. log->end = -1;
  341. spin_lock_init(&log->lock);
  342. dev->dfsentry = e;
  343. snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
  344. e->subdir = debugfs_create_dir(devdir, rootdir);
  345. if (!e->subdir || IS_ERR(e->subdir)) {
  346. if (e->subdir == ERR_PTR(-ENODEV)) {
  347. b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
  348. "enabled in kernel config\n");
  349. } else {
  350. b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n",
  351. devdir);
  352. }
  353. dev->dfsentry = NULL;
  354. kfree(log->log);
  355. kfree(e);
  356. return;
  357. }
  358. #define ADD_FILE(name, mode) \
  359. do { \
  360. struct dentry *d; \
  361. d = debugfs_create_file(__stringify(name), \
  362. mode, e->subdir, dev, \
  363. &fops_##name.fops); \
  364. e->file_##name.dentry = NULL; \
  365. if (!IS_ERR(d)) \
  366. e->file_##name.dentry = d; \
  367. } while (0)
  368. ADD_FILE(tsf, 0600);
  369. ADD_FILE(ucode_regs, 0400);
  370. ADD_FILE(shm, 0400);
  371. ADD_FILE(txstat, 0400);
  372. ADD_FILE(restart, 0200);
  373. #undef ADD_FILE
  374. b43legacy_add_dynamic_debug(dev);
  375. }
  376. void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev)
  377. {
  378. struct b43legacy_dfsentry *e;
  379. if (!dev)
  380. return;
  381. e = dev->dfsentry;
  382. if (!e)
  383. return;
  384. b43legacy_remove_dynamic_debug(dev);
  385. debugfs_remove(e->file_tsf.dentry);
  386. debugfs_remove(e->file_ucode_regs.dentry);
  387. debugfs_remove(e->file_shm.dentry);
  388. debugfs_remove(e->file_txstat.dentry);
  389. debugfs_remove(e->file_restart.dentry);
  390. debugfs_remove(e->subdir);
  391. kfree(e->txstatlog.log);
  392. kfree(e);
  393. }
  394. void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
  395. const struct b43legacy_txstatus *status)
  396. {
  397. struct b43legacy_dfsentry *e = dev->dfsentry;
  398. struct b43legacy_txstatus_log *log;
  399. struct b43legacy_txstatus *cur;
  400. int i;
  401. if (!e)
  402. return;
  403. log = &e->txstatlog;
  404. B43legacy_WARN_ON(!irqs_disabled());
  405. spin_lock(&log->lock);
  406. i = log->end + 1;
  407. if (i == B43legacy_NR_LOGGED_TXSTATUS)
  408. i = 0;
  409. log->end = i;
  410. cur = &(log->log[i]);
  411. memcpy(cur, status, sizeof(*cur));
  412. spin_unlock(&log->lock);
  413. }
  414. void b43legacy_debugfs_init(void)
  415. {
  416. rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
  417. if (IS_ERR(rootdir))
  418. rootdir = NULL;
  419. }
  420. void b43legacy_debugfs_exit(void)
  421. {
  422. debugfs_remove(rootdir);
  423. }