debugfs.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. Broadcom B43legacy wireless driver
  3. debugfs driver debugging code
  4. Copyright (c) 2005-2007 Michael Buesch <mb@bu3sch.de>
  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 int b43legacy_debugfs_open(struct inode *inode, struct file *file)
  163. {
  164. file->private_data = inode->i_private;
  165. return 0;
  166. }
  167. static ssize_t b43legacy_debugfs_read(struct file *file, char __user *userbuf,
  168. size_t count, loff_t *ppos)
  169. {
  170. struct b43legacy_wldev *dev;
  171. struct b43legacy_debugfs_fops *dfops;
  172. struct b43legacy_dfs_file *dfile;
  173. ssize_t uninitialized_var(ret);
  174. char *buf;
  175. const size_t bufsize = 1024 * 16; /* 16 KiB buffer */
  176. const size_t buforder = get_order(bufsize);
  177. int err = 0;
  178. if (!count)
  179. return 0;
  180. dev = file->private_data;
  181. if (!dev)
  182. return -ENODEV;
  183. mutex_lock(&dev->wl->mutex);
  184. if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
  185. err = -ENODEV;
  186. goto out_unlock;
  187. }
  188. dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops);
  189. if (!dfops->read) {
  190. err = -ENOSYS;
  191. goto out_unlock;
  192. }
  193. dfile = fops_to_dfs_file(dev, dfops);
  194. if (!dfile->buffer) {
  195. buf = (char *)__get_free_pages(GFP_KERNEL, buforder);
  196. if (!buf) {
  197. err = -ENOMEM;
  198. goto out_unlock;
  199. }
  200. memset(buf, 0, bufsize);
  201. if (dfops->take_irqlock) {
  202. spin_lock_irq(&dev->wl->irq_lock);
  203. ret = dfops->read(dev, buf, bufsize);
  204. spin_unlock_irq(&dev->wl->irq_lock);
  205. } else
  206. ret = dfops->read(dev, buf, bufsize);
  207. if (ret <= 0) {
  208. free_pages((unsigned long)buf, buforder);
  209. err = ret;
  210. goto out_unlock;
  211. }
  212. dfile->data_len = ret;
  213. dfile->buffer = buf;
  214. }
  215. ret = simple_read_from_buffer(userbuf, count, ppos,
  216. dfile->buffer,
  217. dfile->data_len);
  218. if (*ppos >= dfile->data_len) {
  219. free_pages((unsigned long)dfile->buffer, buforder);
  220. dfile->buffer = NULL;
  221. dfile->data_len = 0;
  222. }
  223. out_unlock:
  224. mutex_unlock(&dev->wl->mutex);
  225. return err ? err : ret;
  226. }
  227. static ssize_t b43legacy_debugfs_write(struct file *file,
  228. const char __user *userbuf,
  229. size_t count, loff_t *ppos)
  230. {
  231. struct b43legacy_wldev *dev;
  232. struct b43legacy_debugfs_fops *dfops;
  233. char *buf;
  234. int err = 0;
  235. if (!count)
  236. return 0;
  237. if (count > PAGE_SIZE)
  238. return -E2BIG;
  239. dev = file->private_data;
  240. if (!dev)
  241. return -ENODEV;
  242. mutex_lock(&dev->wl->mutex);
  243. if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) {
  244. err = -ENODEV;
  245. goto out_unlock;
  246. }
  247. dfops = container_of(file->f_op, struct b43legacy_debugfs_fops, fops);
  248. if (!dfops->write) {
  249. err = -ENOSYS;
  250. goto out_unlock;
  251. }
  252. buf = (char *)get_zeroed_page(GFP_KERNEL);
  253. if (!buf) {
  254. err = -ENOMEM;
  255. goto out_unlock;
  256. }
  257. if (copy_from_user(buf, userbuf, count)) {
  258. err = -EFAULT;
  259. goto out_freepage;
  260. }
  261. if (dfops->take_irqlock) {
  262. spin_lock_irq(&dev->wl->irq_lock);
  263. err = dfops->write(dev, buf, count);
  264. spin_unlock_irq(&dev->wl->irq_lock);
  265. } else
  266. err = dfops->write(dev, buf, count);
  267. if (err)
  268. goto out_freepage;
  269. out_freepage:
  270. free_page((unsigned long)buf);
  271. out_unlock:
  272. mutex_unlock(&dev->wl->mutex);
  273. return err ? err : count;
  274. }
  275. #define B43legacy_DEBUGFS_FOPS(name, _read, _write, _take_irqlock) \
  276. static struct b43legacy_debugfs_fops fops_##name = { \
  277. .read = _read, \
  278. .write = _write, \
  279. .fops = { \
  280. .open = b43legacy_debugfs_open, \
  281. .read = b43legacy_debugfs_read, \
  282. .write = b43legacy_debugfs_write, \
  283. }, \
  284. .file_struct_offset = offsetof(struct b43legacy_dfsentry, \
  285. file_##name), \
  286. .take_irqlock = _take_irqlock, \
  287. }
  288. B43legacy_DEBUGFS_FOPS(tsf, tsf_read_file, tsf_write_file, 1);
  289. B43legacy_DEBUGFS_FOPS(ucode_regs, ucode_regs_read_file, NULL, 1);
  290. B43legacy_DEBUGFS_FOPS(shm, shm_read_file, NULL, 1);
  291. B43legacy_DEBUGFS_FOPS(txstat, txstat_read_file, NULL, 0);
  292. B43legacy_DEBUGFS_FOPS(restart, NULL, restart_write_file, 1);
  293. int b43legacy_debug(struct b43legacy_wldev *dev, enum b43legacy_dyndbg feature)
  294. {
  295. return !!(dev->dfsentry && dev->dfsentry->dyn_debug[feature]);
  296. }
  297. static void b43legacy_remove_dynamic_debug(struct b43legacy_wldev *dev)
  298. {
  299. struct b43legacy_dfsentry *e = dev->dfsentry;
  300. int i;
  301. for (i = 0; i < __B43legacy_NR_DYNDBG; i++)
  302. debugfs_remove(e->dyn_debug_dentries[i]);
  303. }
  304. static void b43legacy_add_dynamic_debug(struct b43legacy_wldev *dev)
  305. {
  306. struct b43legacy_dfsentry *e = dev->dfsentry;
  307. struct dentry *d;
  308. #define add_dyn_dbg(name, id, initstate) do { \
  309. e->dyn_debug[id] = (initstate); \
  310. d = debugfs_create_bool(name, 0600, e->subdir, \
  311. &(e->dyn_debug[id])); \
  312. if (!IS_ERR(d)) \
  313. e->dyn_debug_dentries[id] = d; \
  314. } while (0)
  315. add_dyn_dbg("debug_xmitpower", B43legacy_DBG_XMITPOWER, 0);
  316. add_dyn_dbg("debug_dmaoverflow", B43legacy_DBG_DMAOVERFLOW, 0);
  317. add_dyn_dbg("debug_dmaverbose", B43legacy_DBG_DMAVERBOSE, 0);
  318. add_dyn_dbg("debug_pwork_fast", B43legacy_DBG_PWORK_FAST, 0);
  319. add_dyn_dbg("debug_pwork_stop", B43legacy_DBG_PWORK_STOP, 0);
  320. #undef add_dyn_dbg
  321. }
  322. void b43legacy_debugfs_add_device(struct b43legacy_wldev *dev)
  323. {
  324. struct b43legacy_dfsentry *e;
  325. struct b43legacy_txstatus_log *log;
  326. char devdir[16];
  327. B43legacy_WARN_ON(!dev);
  328. e = kzalloc(sizeof(*e), GFP_KERNEL);
  329. if (!e) {
  330. b43legacyerr(dev->wl, "debugfs: add device OOM\n");
  331. return;
  332. }
  333. e->dev = dev;
  334. log = &e->txstatlog;
  335. log->log = kcalloc(B43legacy_NR_LOGGED_TXSTATUS,
  336. sizeof(struct b43legacy_txstatus), GFP_KERNEL);
  337. if (!log->log) {
  338. b43legacyerr(dev->wl, "debugfs: add device txstatus OOM\n");
  339. kfree(e);
  340. return;
  341. }
  342. log->end = -1;
  343. spin_lock_init(&log->lock);
  344. dev->dfsentry = e;
  345. snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy));
  346. e->subdir = debugfs_create_dir(devdir, rootdir);
  347. if (!e->subdir || IS_ERR(e->subdir)) {
  348. if (e->subdir == ERR_PTR(-ENODEV)) {
  349. b43legacydbg(dev->wl, "DebugFS (CONFIG_DEBUG_FS) not "
  350. "enabled in kernel config\n");
  351. } else {
  352. b43legacyerr(dev->wl, "debugfs: cannot create %s directory\n",
  353. devdir);
  354. }
  355. dev->dfsentry = NULL;
  356. kfree(log->log);
  357. kfree(e);
  358. return;
  359. }
  360. #define ADD_FILE(name, mode) \
  361. do { \
  362. struct dentry *d; \
  363. d = debugfs_create_file(__stringify(name), \
  364. mode, e->subdir, dev, \
  365. &fops_##name.fops); \
  366. e->file_##name.dentry = NULL; \
  367. if (!IS_ERR(d)) \
  368. e->file_##name.dentry = d; \
  369. } while (0)
  370. ADD_FILE(tsf, 0600);
  371. ADD_FILE(ucode_regs, 0400);
  372. ADD_FILE(shm, 0400);
  373. ADD_FILE(txstat, 0400);
  374. ADD_FILE(restart, 0200);
  375. #undef ADD_FILE
  376. b43legacy_add_dynamic_debug(dev);
  377. }
  378. void b43legacy_debugfs_remove_device(struct b43legacy_wldev *dev)
  379. {
  380. struct b43legacy_dfsentry *e;
  381. if (!dev)
  382. return;
  383. e = dev->dfsentry;
  384. if (!e)
  385. return;
  386. b43legacy_remove_dynamic_debug(dev);
  387. debugfs_remove(e->file_tsf.dentry);
  388. debugfs_remove(e->file_ucode_regs.dentry);
  389. debugfs_remove(e->file_shm.dentry);
  390. debugfs_remove(e->file_txstat.dentry);
  391. debugfs_remove(e->file_restart.dentry);
  392. debugfs_remove(e->subdir);
  393. kfree(e->txstatlog.log);
  394. kfree(e);
  395. }
  396. void b43legacy_debugfs_log_txstat(struct b43legacy_wldev *dev,
  397. const struct b43legacy_txstatus *status)
  398. {
  399. struct b43legacy_dfsentry *e = dev->dfsentry;
  400. struct b43legacy_txstatus_log *log;
  401. struct b43legacy_txstatus *cur;
  402. int i;
  403. if (!e)
  404. return;
  405. log = &e->txstatlog;
  406. B43legacy_WARN_ON(!irqs_disabled());
  407. spin_lock(&log->lock);
  408. i = log->end + 1;
  409. if (i == B43legacy_NR_LOGGED_TXSTATUS)
  410. i = 0;
  411. log->end = i;
  412. cur = &(log->log[i]);
  413. memcpy(cur, status, sizeof(*cur));
  414. spin_unlock(&log->lock);
  415. }
  416. void b43legacy_debugfs_init(void)
  417. {
  418. rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL);
  419. if (IS_ERR(rootdir))
  420. rootdir = NULL;
  421. }
  422. void b43legacy_debugfs_exit(void)
  423. {
  424. debugfs_remove(rootdir);
  425. }