debugfs.c 12 KB

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